Esempio n. 1
0
 function ParseID3v2Frame(&$parsedFrame, &$ThisFileInfo)
 {
     // shortcuts
     $id3v2_majorversion = $ThisFileInfo['id3v2']['majorversion'];
     $parsedFrame['framenamelong'] = $this->FrameNameLongLookup($parsedFrame['frame_name']);
     if (empty($parsedFrame['framenamelong'])) {
         unset($parsedFrame['framenamelong']);
     }
     $parsedFrame['framenameshort'] = $this->FrameNameShortLookup($parsedFrame['frame_name']);
     if (empty($parsedFrame['framenameshort'])) {
         unset($parsedFrame['framenameshort']);
     }
     if ($id3v2_majorversion >= 3) {
         // frame flags are not part of the ID3v2.2 standard
         if ($id3v2_majorversion == 3) {
             //    Frame Header Flags
             //    %abc00000 %ijk00000
             $parsedFrame['flags']['TagAlterPreservation'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x8000);
             // a - Tag alter preservation
             $parsedFrame['flags']['FileAlterPreservation'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x4000);
             // b - File alter preservation
             $parsedFrame['flags']['ReadOnly'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x2000);
             // c - Read only
             $parsedFrame['flags']['compression'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x80);
             // i - Compression
             $parsedFrame['flags']['Encryption'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x40);
             // j - Encryption
             $parsedFrame['flags']['GroupingIdentity'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x20);
             // k - Grouping identity
         } elseif ($id3v2_majorversion == 4) {
             //    Frame Header Flags
             //    %0abc0000 %0h00kmnp
             $parsedFrame['flags']['TagAlterPreservation'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x4000);
             // a - Tag alter preservation
             $parsedFrame['flags']['FileAlterPreservation'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x2000);
             // b - File alter preservation
             $parsedFrame['flags']['ReadOnly'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x1000);
             // c - Read only
             $parsedFrame['flags']['GroupingIdentity'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x40);
             // h - Grouping identity
             $parsedFrame['flags']['compression'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x8);
             // k - Compression
             $parsedFrame['flags']['Encryption'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x4);
             // m - Encryption
             $parsedFrame['flags']['Unsynchronisation'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x2);
             // n - Unsynchronisation
             $parsedFrame['flags']['DataLengthIndicator'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x1);
             // p - Data length indicator
             // Frame-level de-unsynchronisation - ID3v2.4
             if ($parsedFrame['flags']['Unsynchronisation']) {
                 $parsedFrame['data'] = $this->DeUnsynchronise($parsedFrame['data']);
             }
         }
         //    Frame-level de-compression
         if ($parsedFrame['flags']['compression']) {
             $parsedFrame['decompressed_size'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], 0, 4));
             if (!function_exists('gzuncompress')) {
                 $ThisFileInfo['warning'][] = 'gzuncompress() support required to decompress ID3v2 frame "' . $parsedFrame['frame_name'] . '"';
             } elseif ($decompresseddata = @gzuncompress(substr($parsedFrame['data'], 4))) {
                 $parsedFrame['data'] = $decompresseddata;
             } else {
                 $ThisFileInfo['warning'][] = 'gzuncompress() failed on compressed contents of ID3v2 frame "' . $parsedFrame['frame_name'] . '"';
             }
         }
     }
     if (isset($parsedFrame['datalength']) && $parsedFrame['datalength'] == 0) {
         $warning = 'Frame "' . $parsedFrame['frame_name'] . '" at offset ' . $parsedFrame['dataoffset'] . ' has no data portion';
         switch ($parsedFrame['frame_name']) {
             case 'WCOM':
                 $warning .= ' (this is known to happen with files tagged by RioPort)';
                 break;
             default:
                 break;
         }
         $ThisFileInfo['warning'][] = $warning;
     } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'UFID' || $id3v2_majorversion == 2 && $parsedFrame['frame_name'] == 'UFI') {
         // 4.1   UFI  Unique file identifier
         //   There may be more than one 'UFID' frame in a tag,
         //   but only one with the same 'Owner identifier'.
         // <Header for 'Unique file identifier', ID: 'UFID'>
         // Owner identifier        <text string> $00
         // Identifier              <up to 64 bytes binary data>
         $frame_terminatorpos = strpos($parsedFrame['data'], "");
         $frame_idstring = substr($parsedFrame['data'], 0, $frame_terminatorpos);
         $parsedFrame['ownerid'] = $frame_idstring;
         $parsedFrame['data'] = substr($parsedFrame['data'], $frame_terminatorpos + strlen(""));
         unset($parsedFrame['data']);
     } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'TXXX' || $id3v2_majorversion == 2 && $parsedFrame['frame_name'] == 'TXX') {
         // 4.2.2 TXX  User defined text information frame
         //   There may be more than one 'TXXX' frame in each tag,
         //   but only one with the same description.
         // <Header for 'User defined text information frame', ID: 'TXXX'>
         // Text encoding     $xx
         // Description       <text string according to encoding> $00 (00)
         // Value             <text string according to encoding>
         $frame_offset = 0;
         $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1));
         if ($id3v2_majorversion <= 3 && $frame_textencoding > 1 || $id3v2_majorversion == 4 && $frame_textencoding > 3) {
             $ThisFileInfo['warning'][] = 'Invalid text encoding byte (' . $frame_textencoding . ') in frame "' . $parsedFrame['frame_name'] . '" - defaulting to ISO-8859-1 encoding';
         }
         $frame_terminatorpos = @strpos($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding), $frame_offset);
         if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) {
             $frame_terminatorpos++;
             // @strpos() fooled because 2nd byte of Unicode chars are often 0x00
         }
         $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
         if (ord($frame_description) === 0) {
             $frame_description = '';
         }
         $parsedFrame['encodingid'] = $frame_textencoding;
         $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding);
         $parsedFrame['description'] = $frame_description;
         $parsedFrame['data'] = substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)));
         if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) {
             $ThisFileInfo['id3v2']['comments'][$parsedFrame['framenameshort']][] = trim(getid3_lib::iconv_fallback($parsedFrame['encoding'], $ThisFileInfo['id3v2']['encoding'], $parsedFrame['data']));
         }
         unset($parsedFrame['data']);
     } elseif ($parsedFrame['frame_name'][0] == 'T') {
         // 4.2. T??[?] Text information frame
         //   There may only be one text information frame of its kind in an tag.
         // <Header for 'Text information frame', ID: 'T000' - 'TZZZ',
         // excluding 'TXXX' described in 4.2.6.>
         // Text encoding                $xx
         // Information                  <text string(s) according to encoding>
         $frame_offset = 0;
         $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1));
         if ($id3v2_majorversion <= 3 && $frame_textencoding > 1 || $id3v2_majorversion == 4 && $frame_textencoding > 3) {
             $ThisFileInfo['warning'][] = 'Invalid text encoding byte (' . $frame_textencoding . ') in frame "' . $parsedFrame['frame_name'] . '" - defaulting to ISO-8859-1 encoding';
         }
         $parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset);
         $parsedFrame['encodingid'] = $frame_textencoding;
         $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding);
         if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) {
             // remove possible terminating \x00 (put by encoding id or software bug)
             $string = getid3_lib::iconv_fallback($parsedFrame['encoding'], $ThisFileInfo['id3v2']['encoding'], $parsedFrame['data']);
             if ($string[strlen($string) - 1] == "") {
                 $string = substr($string, 0, strlen($string) - 1);
             }
             $ThisFileInfo['id3v2']['comments'][$parsedFrame['framenameshort']][] = $string;
             unset($string);
         }
     } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'WXXX' || $id3v2_majorversion == 2 && $parsedFrame['frame_name'] == 'WXX') {
         // 4.3.2 WXX  User defined URL link frame
         //   There may be more than one 'WXXX' frame in each tag,
         //   but only one with the same description
         // <Header for 'User defined URL link frame', ID: 'WXXX'>
         // Text encoding     $xx
         // Description       <text string according to encoding> $00 (00)
         // URL               <text string>
         $frame_offset = 0;
         $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1));
         if ($id3v2_majorversion <= 3 && $frame_textencoding > 1 || $id3v2_majorversion == 4 && $frame_textencoding > 3) {
             $ThisFileInfo['warning'][] = 'Invalid text encoding byte (' . $frame_textencoding . ') in frame "' . $parsedFrame['frame_name'] . '" - defaulting to ISO-8859-1 encoding';
         }
         $frame_terminatorpos = @strpos($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding), $frame_offset);
         if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) {
             $frame_terminatorpos++;
             // @strpos() fooled because 2nd byte of Unicode chars are often 0x00
         }
         $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
         if (ord($frame_description) === 0) {
             $frame_description = '';
         }
         $parsedFrame['data'] = substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)));
         $frame_terminatorpos = strpos($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding));
         if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) {
             $frame_terminatorpos++;
             // strpos() fooled because 2nd byte of Unicode chars are often 0x00
         }
         if ($frame_terminatorpos) {
             // there are null bytes after the data - this is not according to spec
             // only use data up to first null byte
             $frame_urldata = (string) substr($parsedFrame['data'], 0, $frame_terminatorpos);
         } else {
             // no null bytes following data, just use all data
             $frame_urldata = (string) $parsedFrame['data'];
         }
         $parsedFrame['encodingid'] = $frame_textencoding;
         $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding);
         $parsedFrame['url'] = $frame_urldata;
         $parsedFrame['description'] = $frame_description;
         if (!empty($parsedFrame['framenameshort']) && $parsedFrame['url']) {
             $ThisFileInfo['id3v2']['comments'][$parsedFrame['framenameshort']][] = getid3_lib::iconv_fallback($parsedFrame['encoding'], $ThisFileInfo['id3v2']['encoding'], $parsedFrame['url']);
         }
         unset($parsedFrame['data']);
     } elseif ($parsedFrame['frame_name'][0] == 'W') {
         // 4.3. W??? URL link frames
         //   There may only be one URL link frame of its kind in a tag,
         //   except when stated otherwise in the frame description
         // <Header for 'URL link frame', ID: 'W000' - 'WZZZ', excluding 'WXXX'
         // described in 4.3.2.>
         // URL              <text string>
         $parsedFrame['url'] = trim($parsedFrame['data']);
         if (!empty($parsedFrame['framenameshort']) && $parsedFrame['url']) {
             $ThisFileInfo['id3v2']['comments'][$parsedFrame['framenameshort']][] = $parsedFrame['url'];
         }
         unset($parsedFrame['data']);
     } elseif ($id3v2_majorversion == 3 && $parsedFrame['frame_name'] == 'IPLS' || $id3v2_majorversion == 2 && $parsedFrame['frame_name'] == 'IPL') {
         // 4.4  IPL  Involved people list (ID3v2.2 only)
         //   There may only be one 'IPL' frame in each tag
         // <Header for 'User defined URL link frame', ID: 'IPL'>
         // Text encoding     $xx
         // People list strings    <textstrings>
         $frame_offset = 0;
         $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1));
         if ($id3v2_majorversion <= 3 && $frame_textencoding > 1 || $id3v2_majorversion == 4 && $frame_textencoding > 3) {
             $ThisFileInfo['warning'][] = 'Invalid text encoding byte (' . $frame_textencoding . ') in frame "' . $parsedFrame['frame_name'] . '" - defaulting to ISO-8859-1 encoding';
         }
         $parsedFrame['encodingid'] = $frame_textencoding;
         $parsedFrame['encoding'] = $this->TextEncodingNameLookup($parsedFrame['encodingid']);
         $parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset);
         if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) {
             $ThisFileInfo['id3v2']['comments'][$parsedFrame['framenameshort']][] = getid3_lib::iconv_fallback($parsedFrame['encoding'], $ThisFileInfo['id3v2']['encoding'], $parsedFrame['data']);
         }
     } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'MCDI' || $id3v2_majorversion == 2 && $parsedFrame['frame_name'] == 'MCI') {
         // 4.5   MCI  Music CD identifier
         //   There may only be one 'MCDI' frame in each tag
         // <Header for 'Music CD identifier', ID: 'MCDI'>
         // CD TOC                <binary data>
         if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) {
             $ThisFileInfo['id3v2']['comments'][$parsedFrame['framenameshort']][] = $parsedFrame['data'];
         }
     } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'ETCO' || $id3v2_majorversion == 2 && $parsedFrame['frame_name'] == 'ETC') {
         // 4.6   ETC  Event timing codes
         //   There may only be one 'ETCO' frame in each tag
         // <Header for 'Event timing codes', ID: 'ETCO'>
         // Time stamp format    $xx
         //   Where time stamp format is:
         // $01  (32-bit value) MPEG frames from beginning of file
         // $02  (32-bit value) milliseconds from beginning of file
         //   Followed by a list of key events in the following format:
         // Type of event   $xx
         // Time stamp      $xx (xx ...)
         //   The 'Time stamp' is set to zero if directly at the beginning of the sound
         //   or after the previous event. All events MUST be sorted in chronological order.
         $frame_offset = 0;
         $parsedFrame['timestampformat'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
         while ($frame_offset < strlen($parsedFrame['data'])) {
             $parsedFrame['typeid'] = substr($parsedFrame['data'], $frame_offset++, 1);
             $parsedFrame['type'] = $this->ETCOEventLookup($parsedFrame['typeid']);
             $parsedFrame['timestamp'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4));
             $frame_offset += 4;
         }
         unset($parsedFrame['data']);
     } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'MLLT' || $id3v2_majorversion == 2 && $parsedFrame['frame_name'] == 'MLL') {
         // 4.7   MLL MPEG location lookup table
         //   There may only be one 'MLLT' frame in each tag
         // <Header for 'Location lookup table', ID: 'MLLT'>
         // MPEG frames between reference  $xx xx
         // Bytes between reference        $xx xx xx
         // Milliseconds between reference $xx xx xx
         // Bits for bytes deviation       $xx
         // Bits for milliseconds dev.     $xx
         //   Then for every reference the following data is included;
         // Deviation in bytes         %xxx....
         // Deviation in milliseconds  %xxx....
         $frame_offset = 0;
         $parsedFrame['framesbetweenreferences'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], 0, 2));
         $parsedFrame['bytesbetweenreferences'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], 2, 3));
         $parsedFrame['msbetweenreferences'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], 5, 3));
         $parsedFrame['bitsforbytesdeviation'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], 8, 1));
         $parsedFrame['bitsformsdeviation'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], 9, 1));
         $parsedFrame['data'] = substr($parsedFrame['data'], 10);
         while ($frame_offset < strlen($parsedFrame['data'])) {
             $deviationbitstream .= getid3_lib::BigEndian2Bin(substr($parsedFrame['data'], $frame_offset++, 1));
         }
         $reference_counter = 0;
         while (strlen($deviationbitstream) > 0) {
             $parsedFrame[$reference_counter]['bytedeviation'] = bindec(substr($deviationbitstream, 0, $parsedFrame['bitsforbytesdeviation']));
             $parsedFrame[$reference_counter]['msdeviation'] = bindec(substr($deviationbitstream, $parsedFrame['bitsforbytesdeviation'], $parsedFrame['bitsformsdeviation']));
             $deviationbitstream = substr($deviationbitstream, $parsedFrame['bitsforbytesdeviation'] + $parsedFrame['bitsformsdeviation']);
             $reference_counter++;
         }
         unset($parsedFrame['data']);
     } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'SYTC' || $id3v2_majorversion == 2 && $parsedFrame['frame_name'] == 'STC') {
         // 4.8   STC  Synchronised tempo codes
         //   There may only be one 'SYTC' frame in each tag
         // <Header for 'Synchronised tempo codes', ID: 'SYTC'>
         // Time stamp format   $xx
         // Tempo data          <binary data>
         //   Where time stamp format is:
         // $01  (32-bit value) MPEG frames from beginning of file
         // $02  (32-bit value) milliseconds from beginning of file
         $frame_offset = 0;
         $parsedFrame['timestampformat'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
         $timestamp_counter = 0;
         while ($frame_offset < strlen($parsedFrame['data'])) {
             $parsedFrame[$timestamp_counter]['tempo'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
             if ($parsedFrame[$timestamp_counter]['tempo'] == 255) {
                 $parsedFrame[$timestamp_counter]['tempo'] += ord(substr($parsedFrame['data'], $frame_offset++, 1));
             }
             $parsedFrame[$timestamp_counter]['timestamp'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4));
             $frame_offset += 4;
             $timestamp_counter++;
         }
         unset($parsedFrame['data']);
     } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'USLT' || $id3v2_majorversion == 2 && $parsedFrame['frame_name'] == 'ULT') {
         // 4.9   ULT  Unsynchronised lyric/text transcription
         //   There may be more than one 'Unsynchronised lyrics/text transcription' frame
         //   in each tag, but only one with the same language and content descriptor.
         // <Header for 'Unsynchronised lyrics/text transcription', ID: 'USLT'>
         // Text encoding        $xx
         // Language             $xx xx xx
         // Content descriptor   <text string according to encoding> $00 (00)
         // Lyrics/text          <full text string according to encoding>
         $frame_offset = 0;
         $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1));
         if ($id3v2_majorversion <= 3 && $frame_textencoding > 1 || $id3v2_majorversion == 4 && $frame_textencoding > 3) {
             $ThisFileInfo['warning'][] = 'Invalid text encoding byte (' . $frame_textencoding . ') in frame "' . $parsedFrame['frame_name'] . '" - defaulting to ISO-8859-1 encoding';
         }
         $frame_language = substr($parsedFrame['data'], $frame_offset, 3);
         $frame_offset += 3;
         $frame_terminatorpos = @strpos($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding), $frame_offset);
         if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) {
             $frame_terminatorpos++;
             // @strpos() fooled because 2nd byte of Unicode chars are often 0x00
         }
         $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
         if (ord($frame_description) === 0) {
             $frame_description = '';
         }
         $parsedFrame['data'] = substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)));
         $parsedFrame['encodingid'] = $frame_textencoding;
         $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding);
         $parsedFrame['data'] = $parsedFrame['data'];
         $parsedFrame['language'] = $frame_language;
         $parsedFrame['languagename'] = $this->LanguageLookup($frame_language, false);
         $parsedFrame['description'] = $frame_description;
         if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) {
             $ThisFileInfo['id3v2']['comments'][$parsedFrame['framenameshort']][] = getid3_lib::iconv_fallback($parsedFrame['encoding'], $ThisFileInfo['id3v2']['encoding'], $parsedFrame['data']);
         }
         unset($parsedFrame['data']);
     } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'SYLT' || $id3v2_majorversion == 2 && $parsedFrame['frame_name'] == 'SLT') {
         // 4.10  SLT  Synchronised lyric/text
         //   There may be more than one 'SYLT' frame in each tag,
         //   but only one with the same language and content descriptor.
         // <Header for 'Synchronised lyrics/text', ID: 'SYLT'>
         // Text encoding        $xx
         // Language             $xx xx xx
         // Time stamp format    $xx
         //   $01  (32-bit value) MPEG frames from beginning of file
         //   $02  (32-bit value) milliseconds from beginning of file
         // Content type         $xx
         // Content descriptor   <text string according to encoding> $00 (00)
         //   Terminated text to be synced (typically a syllable)
         //   Sync identifier (terminator to above string)   $00 (00)
         //   Time stamp                                     $xx (xx ...)
         $frame_offset = 0;
         $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1));
         if ($id3v2_majorversion <= 3 && $frame_textencoding > 1 || $id3v2_majorversion == 4 && $frame_textencoding > 3) {
             $ThisFileInfo['warning'][] = 'Invalid text encoding byte (' . $frame_textencoding . ') in frame "' . $parsedFrame['frame_name'] . '" - defaulting to ISO-8859-1 encoding';
         }
         $frame_language = substr($parsedFrame['data'], $frame_offset, 3);
         $frame_offset += 3;
         $parsedFrame['timestampformat'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
         $parsedFrame['contenttypeid'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
         $parsedFrame['contenttype'] = $this->SYTLContentTypeLookup($parsedFrame['contenttypeid']);
         $parsedFrame['encodingid'] = $frame_textencoding;
         $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding);
         $parsedFrame['language'] = $frame_language;
         $parsedFrame['languagename'] = $this->LanguageLookup($frame_language, false);
         $timestampindex = 0;
         $frame_remainingdata = substr($parsedFrame['data'], $frame_offset);
         while (strlen($frame_remainingdata)) {
             $frame_offset = 0;
             $frame_terminatorpos = strpos($frame_remainingdata, $this->TextEncodingTerminatorLookup($frame_textencoding));
             if ($frame_terminatorpos === false) {
                 $frame_remainingdata = '';
             } else {
                 if (ord(substr($frame_remainingdata, $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) {
                     $frame_terminatorpos++;
                     // strpos() fooled because 2nd byte of Unicode chars are often 0x00
                 }
                 $parsedFrame['lyrics'][$timestampindex]['data'] = substr($frame_remainingdata, $frame_offset, $frame_terminatorpos - $frame_offset);
                 $frame_remainingdata = substr($frame_remainingdata, $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)));
                 if ($timestampindex == 0 && ord($frame_remainingdata[0]) != 0) {
                     // timestamp probably omitted for first data item
                 } else {
                     $parsedFrame['lyrics'][$timestampindex]['timestamp'] = getid3_lib::BigEndian2Int(substr($frame_remainingdata, 0, 4));
                     $frame_remainingdata = substr($frame_remainingdata, 4);
                 }
                 $timestampindex++;
             }
         }
         unset($parsedFrame['data']);
     } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'COMM' || $id3v2_majorversion == 2 && $parsedFrame['frame_name'] == 'COM') {
         // 4.11  COM  Comments
         //   There may be more than one comment frame in each tag,
         //   but only one with the same language and content descriptor.
         // <Header for 'Comment', ID: 'COMM'>
         // Text encoding          $xx
         // Language               $xx xx xx
         // Short content descrip. <text string according to encoding> $00 (00)
         // The actual text        <full text string according to encoding>
         if (strlen($parsedFrame['data']) < 5) {
             $ThisFileInfo['warning'][] = 'Invalid data (too short) for "' . $parsedFrame['frame_name'] . '" frame at offset ' . $parsedFrame['dataoffset'];
         } else {
             $frame_offset = 0;
             $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1));
             if ($id3v2_majorversion <= 3 && $frame_textencoding > 1 || $id3v2_majorversion == 4 && $frame_textencoding > 3) {
                 $ThisFileInfo['warning'][] = 'Invalid text encoding byte (' . $frame_textencoding . ') in frame "' . $parsedFrame['frame_name'] . '" - defaulting to ISO-8859-1 encoding';
             }
             $frame_language = substr($parsedFrame['data'], $frame_offset, 3);
             $frame_offset += 3;
             $frame_terminatorpos = @strpos($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding), $frame_offset);
             if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) {
                 $frame_terminatorpos++;
                 // @strpos() fooled because 2nd byte of Unicode chars are often 0x00
             }
             $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
             if (ord($frame_description) === 0) {
                 $frame_description = '';
             }
             $frame_text = (string) substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)));
             $parsedFrame['encodingid'] = $frame_textencoding;
             $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding);
             $parsedFrame['language'] = $frame_language;
             $parsedFrame['languagename'] = $this->LanguageLookup($frame_language, false);
             $parsedFrame['description'] = $frame_description;
             $parsedFrame['data'] = $frame_text;
             if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) {
                 $ThisFileInfo['id3v2']['comments'][$parsedFrame['framenameshort']][] = getid3_lib::iconv_fallback($parsedFrame['encoding'], $ThisFileInfo['id3v2']['encoding'], $parsedFrame['data']);
             }
         }
     } elseif ($id3v2_majorversion >= 4 && $parsedFrame['frame_name'] == 'RVA2') {
         // 4.11  RVA2 Relative volume adjustment (2) (ID3v2.4+ only)
         //   There may be more than one 'RVA2' frame in each tag,
         //   but only one with the same identification string
         // <Header for 'Relative volume adjustment (2)', ID: 'RVA2'>
         // Identification          <text string> $00
         //   The 'identification' string is used to identify the situation and/or
         //   device where this adjustment should apply. The following is then
         //   repeated for every channel:
         // Type of channel         $xx
         // Volume adjustment       $xx xx
         // Bits representing peak  $xx
         // Peak volume             $xx (xx ...)
         $frame_terminatorpos = strpos($parsedFrame['data'], "");
         $frame_idstring = substr($parsedFrame['data'], 0, $frame_terminatorpos);
         if (ord($frame_idstring) === 0) {
             $frame_idstring = '';
         }
         $frame_remainingdata = substr($parsedFrame['data'], $frame_terminatorpos + strlen(""));
         $parsedFrame['description'] = $frame_idstring;
         while (strlen($frame_remainingdata)) {
             $frame_offset = 0;
             $frame_channeltypeid = ord(substr($frame_remainingdata, $frame_offset++, 1));
             $parsedFrame[$frame_channeltypeid]['channeltypeid'] = $frame_channeltypeid;
             $parsedFrame[$frame_channeltypeid]['channeltype'] = $this->RVA2ChannelTypeLookup($frame_channeltypeid);
             $parsedFrame[$frame_channeltypeid]['volumeadjust'] = getid3_lib::BigEndian2Int(substr($frame_remainingdata, $frame_offset, 2), false, true);
             // 16-bit signed
             $frame_offset += 2;
             $parsedFrame[$frame_channeltypeid]['bitspeakvolume'] = ord(substr($frame_remainingdata, $frame_offset++, 1));
             $frame_bytespeakvolume = ceil($parsedFrame[$frame_channeltypeid]['bitspeakvolume'] / 8);
             $parsedFrame[$frame_channeltypeid]['peakvolume'] = getid3_lib::BigEndian2Int(substr($frame_remainingdata, $frame_offset, $frame_bytespeakvolume));
             $frame_remainingdata = substr($frame_remainingdata, $frame_offset + $frame_bytespeakvolume);
         }
         unset($parsedFrame['data']);
     } elseif ($id3v2_majorversion == 3 && $parsedFrame['frame_name'] == 'RVAD' || $id3v2_majorversion == 2 && $parsedFrame['frame_name'] == 'RVA') {
         // 4.12  RVA  Relative volume adjustment (ID3v2.2 only)
         //   There may only be one 'RVA' frame in each tag
         // <Header for 'Relative volume adjustment', ID: 'RVA'>
         // ID3v2.2 => Increment/decrement     %000000ba
         // ID3v2.3 => Increment/decrement     %00fedcba
         // Bits used for volume descr.        $xx
         // Relative volume change, right      $xx xx (xx ...) // a
         // Relative volume change, left       $xx xx (xx ...) // b
         // Peak volume right                  $xx xx (xx ...)
         // Peak volume left                   $xx xx (xx ...)
         //   ID3v2.3 only, optional (not present in ID3v2.2):
         // Relative volume change, right back $xx xx (xx ...) // c
         // Relative volume change, left back  $xx xx (xx ...) // d
         // Peak volume right back             $xx xx (xx ...)
         // Peak volume left back              $xx xx (xx ...)
         //   ID3v2.3 only, optional (not present in ID3v2.2):
         // Relative volume change, center     $xx xx (xx ...) // e
         // Peak volume center                 $xx xx (xx ...)
         //   ID3v2.3 only, optional (not present in ID3v2.2):
         // Relative volume change, bass       $xx xx (xx ...) // f
         // Peak volume bass                   $xx xx (xx ...)
         $frame_offset = 0;
         $frame_incrdecrflags = getid3_lib::BigEndian2Bin(substr($parsedFrame['data'], $frame_offset++, 1));
         $parsedFrame['incdec']['right'] = (bool) substr($frame_incrdecrflags, 6, 1);
         $parsedFrame['incdec']['left'] = (bool) substr($frame_incrdecrflags, 7, 1);
         $parsedFrame['bitsvolume'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
         $frame_bytesvolume = ceil($parsedFrame['bitsvolume'] / 8);
         $parsedFrame['volumechange']['right'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume));
         if ($parsedFrame['incdec']['right'] === false) {
             $parsedFrame['volumechange']['right'] *= -1;
         }
         $frame_offset += $frame_bytesvolume;
         $parsedFrame['volumechange']['left'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume));
         if ($parsedFrame['incdec']['left'] === false) {
             $parsedFrame['volumechange']['left'] *= -1;
         }
         $frame_offset += $frame_bytesvolume;
         $parsedFrame['peakvolume']['right'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume));
         $frame_offset += $frame_bytesvolume;
         $parsedFrame['peakvolume']['left'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume));
         $frame_offset += $frame_bytesvolume;
         if ($id3v2_majorversion == 3) {
             $parsedFrame['data'] = substr($parsedFrame['data'], $frame_offset);
             if (strlen($parsedFrame['data']) > 0) {
                 $parsedFrame['incdec']['rightrear'] = (bool) substr($frame_incrdecrflags, 4, 1);
                 $parsedFrame['incdec']['leftrear'] = (bool) substr($frame_incrdecrflags, 5, 1);
                 $parsedFrame['volumechange']['rightrear'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume));
                 if ($parsedFrame['incdec']['rightrear'] === false) {
                     $parsedFrame['volumechange']['rightrear'] *= -1;
                 }
                 $frame_offset += $frame_bytesvolume;
                 $parsedFrame['volumechange']['leftrear'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume));
                 if ($parsedFrame['incdec']['leftrear'] === false) {
                     $parsedFrame['volumechange']['leftrear'] *= -1;
                 }
                 $frame_offset += $frame_bytesvolume;
                 $parsedFrame['peakvolume']['rightrear'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume));
                 $frame_offset += $frame_bytesvolume;
                 $parsedFrame['peakvolume']['leftrear'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume));
                 $frame_offset += $frame_bytesvolume;
             }
             $parsedFrame['data'] = substr($parsedFrame['data'], $frame_offset);
             if (strlen($parsedFrame['data']) > 0) {
                 $parsedFrame['incdec']['center'] = (bool) substr($frame_incrdecrflags, 3, 1);
                 $parsedFrame['volumechange']['center'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume));
                 if ($parsedFrame['incdec']['center'] === false) {
                     $parsedFrame['volumechange']['center'] *= -1;
                 }
                 $frame_offset += $frame_bytesvolume;
                 $parsedFrame['peakvolume']['center'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume));
                 $frame_offset += $frame_bytesvolume;
             }
             $parsedFrame['data'] = substr($parsedFrame['data'], $frame_offset);
             if (strlen($parsedFrame['data']) > 0) {
                 $parsedFrame['incdec']['bass'] = (bool) substr($frame_incrdecrflags, 2, 1);
                 $parsedFrame['volumechange']['bass'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume));
                 if ($parsedFrame['incdec']['bass'] === false) {
                     $parsedFrame['volumechange']['bass'] *= -1;
                 }
                 $frame_offset += $frame_bytesvolume;
                 $parsedFrame['peakvolume']['bass'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume));
                 $frame_offset += $frame_bytesvolume;
             }
         }
         unset($parsedFrame['data']);
     } elseif ($id3v2_majorversion >= 4 && $parsedFrame['frame_name'] == 'EQU2') {
         // 4.12  EQU2 Equalisation (2) (ID3v2.4+ only)
         //   There may be more than one 'EQU2' frame in each tag,
         //   but only one with the same identification string
         // <Header of 'Equalisation (2)', ID: 'EQU2'>
         // Interpolation method  $xx
         //   $00  Band
         //   $01  Linear
         // Identification        <text string> $00
         //   The following is then repeated for every adjustment point
         // Frequency          $xx xx
         // Volume adjustment  $xx xx
         $frame_offset = 0;
         $frame_interpolationmethod = ord(substr($parsedFrame['data'], $frame_offset++, 1));
         $frame_terminatorpos = @strpos($parsedFrame['data'], "", $frame_offset);
         $frame_idstring = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
         if (ord($frame_idstring) === 0) {
             $frame_idstring = '';
         }
         $parsedFrame['description'] = $frame_idstring;
         $frame_remainingdata = substr($parsedFrame['data'], $frame_terminatorpos + strlen(""));
         while (strlen($frame_remainingdata)) {
             $frame_frequency = getid3_lib::BigEndian2Int(substr($frame_remainingdata, 0, 2)) / 2;
             $parsedFrame['data'][$frame_frequency] = getid3_lib::BigEndian2Int(substr($frame_remainingdata, 2, 2), false, true);
             $frame_remainingdata = substr($frame_remainingdata, 4);
         }
         $parsedFrame['interpolationmethod'] = $frame_interpolationmethod;
         unset($parsedFrame['data']);
     } elseif ($id3v2_majorversion == 3 && $parsedFrame['frame_name'] == 'EQUA' || $id3v2_majorversion == 2 && $parsedFrame['frame_name'] == 'EQU') {
         // 4.13  EQU  Equalisation (ID3v2.2 only)
         //   There may only be one 'EQUA' frame in each tag
         // <Header for 'Relative volume adjustment', ID: 'EQU'>
         // Adjustment bits    $xx
         //   This is followed by 2 bytes + ('adjustment bits' rounded up to the
         //   nearest byte) for every equalisation band in the following format,
         //   giving a frequency range of 0 - 32767Hz:
         // Increment/decrement   %x (MSB of the Frequency)
         // Frequency             (lower 15 bits)
         // Adjustment            $xx (xx ...)
         $frame_offset = 0;
         $parsedFrame['adjustmentbits'] = substr($parsedFrame['data'], $frame_offset++, 1);
         $frame_adjustmentbytes = ceil($parsedFrame['adjustmentbits'] / 8);
         $frame_remainingdata = (string) substr($parsedFrame['data'], $frame_offset);
         while (strlen($frame_remainingdata) > 0) {
             $frame_frequencystr = getid3_lib::BigEndian2Bin(substr($frame_remainingdata, 0, 2));
             $frame_incdec = (bool) substr($frame_frequencystr, 0, 1);
             $frame_frequency = bindec(substr($frame_frequencystr, 1, 15));
             $parsedFrame[$frame_frequency]['incdec'] = $frame_incdec;
             $parsedFrame[$frame_frequency]['adjustment'] = getid3_lib::BigEndian2Int(substr($frame_remainingdata, 2, $frame_adjustmentbytes));
             if ($parsedFrame[$frame_frequency]['incdec'] === false) {
                 $parsedFrame[$frame_frequency]['adjustment'] *= -1;
             }
             $frame_remainingdata = substr($frame_remainingdata, 2 + $frame_adjustmentbytes);
         }
         unset($parsedFrame['data']);
     } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'RVRB' || $id3v2_majorversion == 2 && $parsedFrame['frame_name'] == 'REV') {
         // 4.14  REV  Reverb
         //   There may only be one 'RVRB' frame in each tag.
         // <Header for 'Reverb', ID: 'RVRB'>
         // Reverb left (ms)                 $xx xx
         // Reverb right (ms)                $xx xx
         // Reverb bounces, left             $xx
         // Reverb bounces, right            $xx
         // Reverb feedback, left to left    $xx
         // Reverb feedback, left to right   $xx
         // Reverb feedback, right to right  $xx
         // Reverb feedback, right to left   $xx
         // Premix left to right             $xx
         // Premix right to left             $xx
         $frame_offset = 0;
         $parsedFrame['left'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 2));
         $frame_offset += 2;
         $parsedFrame['right'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 2));
         $frame_offset += 2;
         $parsedFrame['bouncesL'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
         $parsedFrame['bouncesR'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
         $parsedFrame['feedbackLL'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
         $parsedFrame['feedbackLR'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
         $parsedFrame['feedbackRR'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
         $parsedFrame['feedbackRL'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
         $parsedFrame['premixLR'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
         $parsedFrame['premixRL'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
         unset($parsedFrame['data']);
     } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'APIC' || $id3v2_majorversion == 2 && $parsedFrame['frame_name'] == 'PIC') {
         // 4.15  PIC  Attached picture
         //   There may be several pictures attached to one file,
         //   each in their individual 'APIC' frame, but only one
         //   with the same content descriptor
         // <Header for 'Attached picture', ID: 'APIC'>
         // Text encoding      $xx
         // ID3v2.3+ => MIME type          <text string> $00
         // ID3v2.2  => Image format       $xx xx xx
         // Picture type       $xx
         // Description        <text string according to encoding> $00 (00)
         // Picture data       <binary data>
         $frame_offset = 0;
         $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1));
         if ($id3v2_majorversion <= 3 && $frame_textencoding > 1 || $id3v2_majorversion == 4 && $frame_textencoding > 3) {
             $ThisFileInfo['warning'][] = 'Invalid text encoding byte (' . $frame_textencoding . ') in frame "' . $parsedFrame['frame_name'] . '" - defaulting to ISO-8859-1 encoding';
         }
         if ($id3v2_majorversion == 2 && strlen($parsedFrame['data']) > $frame_offset) {
             $frame_imagetype = substr($parsedFrame['data'], $frame_offset, 3);
             if (strtolower($frame_imagetype) == 'ima') {
                 // complete hack for mp3Rage (www.chaoticsoftware.com) that puts ID3v2.3-formatted
                 // MIME type instead of 3-char ID3v2.2-format image type  (thanks xbhoffØpacbell*net)
                 $frame_terminatorpos = @strpos($parsedFrame['data'], "", $frame_offset);
                 $frame_mimetype = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
                 if (ord($frame_mimetype) === 0) {
                     $frame_mimetype = '';
                 }
                 $frame_imagetype = strtoupper(str_replace('image/', '', strtolower($frame_mimetype)));
                 if ($frame_imagetype == 'JPEG') {
                     $frame_imagetype = 'JPG';
                 }
                 $frame_offset = $frame_terminatorpos + strlen("");
             } else {
                 $frame_offset += 3;
             }
         }
         if ($id3v2_majorversion > 2 && strlen($parsedFrame['data']) > $frame_offset) {
             $frame_terminatorpos = @strpos($parsedFrame['data'], "", $frame_offset);
             $frame_mimetype = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
             if (ord($frame_mimetype) === 0) {
                 $frame_mimetype = '';
             }
             $frame_offset = $frame_terminatorpos + strlen("");
         }
         $frame_picturetype = ord(substr($parsedFrame['data'], $frame_offset++, 1));
         $frame_terminatorpos = @strpos($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding), $frame_offset);
         if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) {
             $frame_terminatorpos++;
             // @strpos() fooled because 2nd byte of Unicode chars are often 0x00
         }
         $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
         if (ord($frame_description) === 0) {
             $frame_description = '';
         }
         $parsedFrame['encodingid'] = $frame_textencoding;
         $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding);
         if ($id3v2_majorversion == 2) {
             $parsedFrame['imagetype'] = $frame_imagetype;
         } else {
             $parsedFrame['mime'] = $frame_mimetype;
         }
         $parsedFrame['picturetypeid'] = $frame_picturetype;
         $parsedFrame['picturetype'] = $this->APICPictureTypeLookup($frame_picturetype);
         $parsedFrame['description'] = $frame_description;
         $parsedFrame['data'] = substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)));
         $imagechunkcheck = getid3_lib::GetDataImageSize($parsedFrame['data']);
         if ($imagechunkcheck[2] >= 1 && $imagechunkcheck[2] <= 3) {
             $parsedFrame['image_mime'] = 'image/' . getid3_lib::ImageTypesLookup($imagechunkcheck[2]);
             if ($imagechunkcheck[0]) {
                 $parsedFrame['image_width'] = $imagechunkcheck[0];
             }
             if ($imagechunkcheck[1]) {
                 $parsedFrame['image_height'] = $imagechunkcheck[1];
             }
             $parsedFrame['image_bytes'] = strlen($parsedFrame['data']);
         }
     } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'GEOB' || $id3v2_majorversion == 2 && $parsedFrame['frame_name'] == 'GEO') {
         // 4.16  GEO  General encapsulated object
         //   There may be more than one 'GEOB' frame in each tag,
         //   but only one with the same content descriptor
         // <Header for 'General encapsulated object', ID: 'GEOB'>
         // Text encoding          $xx
         // MIME type              <text string> $00
         // Filename               <text string according to encoding> $00 (00)
         // Content description    <text string according to encoding> $00 (00)
         // Encapsulated object    <binary data>
         $frame_offset = 0;
         $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1));
         if ($id3v2_majorversion <= 3 && $frame_textencoding > 1 || $id3v2_majorversion == 4 && $frame_textencoding > 3) {
             $ThisFileInfo['warning'][] = 'Invalid text encoding byte (' . $frame_textencoding . ') in frame "' . $parsedFrame['frame_name'] . '" - defaulting to ISO-8859-1 encoding';
         }
         $frame_terminatorpos = @strpos($parsedFrame['data'], "", $frame_offset);
         $frame_mimetype = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
         if (ord($frame_mimetype) === 0) {
             $frame_mimetype = '';
         }
         $frame_offset = $frame_terminatorpos + strlen("");
         $frame_terminatorpos = @strpos($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding), $frame_offset);
         if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) {
             $frame_terminatorpos++;
             // @strpos() fooled because 2nd byte of Unicode chars are often 0x00
         }
         $frame_filename = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
         if (ord($frame_filename) === 0) {
             $frame_filename = '';
         }
         $frame_offset = $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding));
         $frame_terminatorpos = @strpos($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding), $frame_offset);
         if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) {
             $frame_terminatorpos++;
             // @strpos() fooled because 2nd byte of Unicode chars are often 0x00
         }
         $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
         if (ord($frame_description) === 0) {
             $frame_description = '';
         }
         $frame_offset = $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding));
         $parsedFrame['objectdata'] = (string) substr($parsedFrame['data'], $frame_offset);
         $parsedFrame['encodingid'] = $frame_textencoding;
         $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding);
         $parsedFrame['mime'] = $frame_mimetype;
         $parsedFrame['filename'] = $frame_filename;
         $parsedFrame['description'] = $frame_description;
         unset($parsedFrame['data']);
     } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'PCNT' || $id3v2_majorversion == 2 && $parsedFrame['frame_name'] == 'CNT') {
         // 4.17  CNT  Play counter
         //   There may only be one 'PCNT' frame in each tag.
         //   When the counter reaches all one's, one byte is inserted in
         //   front of the counter thus making the counter eight bits bigger
         // <Header for 'Play counter', ID: 'PCNT'>
         // Counter        $xx xx xx xx (xx ...)
         $parsedFrame['data'] = getid3_lib::BigEndian2Int($parsedFrame['data']);
     } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'POPM' || $id3v2_majorversion == 2 && $parsedFrame['frame_name'] == 'POP') {
         // 4.18  POP  Popularimeter
         //   There may be more than one 'POPM' frame in each tag,
         //   but only one with the same email address
         // <Header for 'Popularimeter', ID: 'POPM'>
         // Email to user   <text string> $00
         // Rating          $xx
         // Counter         $xx xx xx xx (xx ...)
         $frame_offset = 0;
         $frame_terminatorpos = @strpos($parsedFrame['data'], "", $frame_offset);
         $frame_emailaddress = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
         if (ord($frame_emailaddress) === 0) {
             $frame_emailaddress = '';
         }
         $frame_offset = $frame_terminatorpos + strlen("");
         $frame_rating = ord(substr($parsedFrame['data'], $frame_offset++, 1));
         $parsedFrame['data'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset));
         $parsedFrame['email'] = $frame_emailaddress;
         $parsedFrame['rating'] = $frame_rating;
         unset($parsedFrame['data']);
     } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'RBUF' || $id3v2_majorversion == 2 && $parsedFrame['frame_name'] == 'BUF') {
         // 4.19  BUF  Recommended buffer size
         //   There may only be one 'RBUF' frame in each tag
         // <Header for 'Recommended buffer size', ID: 'RBUF'>
         // Buffer size               $xx xx xx
         // Embedded info flag        %0000000x
         // Offset to next tag        $xx xx xx xx
         $frame_offset = 0;
         $parsedFrame['buffersize'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 3));
         $frame_offset += 3;
         $frame_embeddedinfoflags = getid3_lib::BigEndian2Bin(substr($parsedFrame['data'], $frame_offset++, 1));
         $parsedFrame['flags']['embededinfo'] = (bool) substr($frame_embeddedinfoflags, 7, 1);
         $parsedFrame['nexttagoffset'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4));
         unset($parsedFrame['data']);
     } elseif ($id3v2_majorversion == 2 && $parsedFrame['frame_name'] == 'CRM') {
         // 4.20  Encrypted meta frame (ID3v2.2 only)
         //   There may be more than one 'CRM' frame in a tag,
         //   but only one with the same 'owner identifier'
         // <Header for 'Encrypted meta frame', ID: 'CRM'>
         // Owner identifier      <textstring> $00 (00)
         // Content/explanation   <textstring> $00 (00)
         // Encrypted datablock   <binary data>
         $frame_offset = 0;
         $frame_terminatorpos = @strpos($parsedFrame['data'], "", $frame_offset);
         $frame_ownerid = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
         $frame_offset = $frame_terminatorpos + strlen("");
         $frame_terminatorpos = @strpos($parsedFrame['data'], "", $frame_offset);
         $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
         if (ord($frame_description) === 0) {
             $frame_description = '';
         }
         $frame_offset = $frame_terminatorpos + strlen("");
         $parsedFrame['ownerid'] = $frame_ownerid;
         $parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset);
         $parsedFrame['description'] = $frame_description;
         unset($parsedFrame['data']);
     } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'AENC' || $id3v2_majorversion == 2 && $parsedFrame['frame_name'] == 'CRA') {
         // 4.21  CRA  Audio encryption
         //   There may be more than one 'AENC' frames in a tag,
         //   but only one with the same 'Owner identifier'
         // <Header for 'Audio encryption', ID: 'AENC'>
         // Owner identifier   <text string> $00
         // Preview start      $xx xx
         // Preview length     $xx xx
         // Encryption info    <binary data>
         $frame_offset = 0;
         $frame_terminatorpos = @strpos($parsedFrame['data'], "", $frame_offset);
         $frame_ownerid = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
         if (ord($frame_ownerid) === 0) {
             $frame_ownerid == '';
         }
         $frame_offset = $frame_terminatorpos + strlen("");
         $parsedFrame['ownerid'] = $frame_ownerid;
         $parsedFrame['previewstart'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 2));
         $frame_offset += 2;
         $parsedFrame['previewlength'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 2));
         $frame_offset += 2;
         $parsedFrame['encryptioninfo'] = (string) substr($parsedFrame['data'], $frame_offset);
         unset($parsedFrame['data']);
     } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'LINK' || $id3v2_majorversion == 2 && $parsedFrame['frame_name'] == 'LNK') {
         // 4.22  LNK  Linked information
         //   There may be more than one 'LINK' frame in a tag,
         //   but only one with the same contents
         // <Header for 'Linked information', ID: 'LINK'>
         // ID3v2.3+ => Frame identifier   $xx xx xx xx
         // ID3v2.2  => Frame identifier   $xx xx xx
         // URL                            <text string> $00
         // ID and additional data         <text string(s)>
         $frame_offset = 0;
         if ($id3v2_majorversion == 2) {
             $parsedFrame['frameid'] = substr($parsedFrame['data'], $frame_offset, 3);
             $frame_offset += 3;
         } else {
             $parsedFrame['frameid'] = substr($parsedFrame['data'], $frame_offset, 4);
             $frame_offset += 4;
         }
         $frame_terminatorpos = @strpos($parsedFrame['data'], "", $frame_offset);
         $frame_url = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
         if (ord($frame_url) === 0) {
             $frame_url = '';
         }
         $frame_offset = $frame_terminatorpos + strlen("");
         $parsedFrame['url'] = $frame_url;
         $parsedFrame['additionaldata'] = (string) substr($parsedFrame['data'], $frame_offset);
         if (!empty($parsedFrame['framenameshort']) && $parsedFrame['url']) {
             $ThisFileInfo['id3v2']['comments'][$parsedFrame['framenameshort']][] = utf8_encode($parsedFrame['url']);
         }
         unset($parsedFrame['data']);
     } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'POSS') {
         // 4.21  POSS Position synchronisation frame (ID3v2.3+ only)
         //   There may only be one 'POSS' frame in each tag
         // <Head for 'Position synchronisation', ID: 'POSS'>
         // Time stamp format         $xx
         // Position                  $xx (xx ...)
         $frame_offset = 0;
         $parsedFrame['timestampformat'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
         $parsedFrame['position'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset));
         unset($parsedFrame['data']);
     } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'USER') {
         // 4.22  USER Terms of use (ID3v2.3+ only)
         //   There may be more than one 'Terms of use' frame in a tag,
         //   but only one with the same 'Language'
         // <Header for 'Terms of use frame', ID: 'USER'>
         // Text encoding        $xx
         // Language             $xx xx xx
         // The actual text      <text string according to encoding>
         $frame_offset = 0;
         $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1));
         if ($id3v2_majorversion <= 3 && $frame_textencoding > 1 || $id3v2_majorversion == 4 && $frame_textencoding > 3) {
             $ThisFileInfo['warning'][] = 'Invalid text encoding byte (' . $frame_textencoding . ') in frame "' . $parsedFrame['frame_name'] . '" - defaulting to ISO-8859-1 encoding';
         }
         $frame_language = substr($parsedFrame['data'], $frame_offset, 3);
         $frame_offset += 3;
         $parsedFrame['language'] = $frame_language;
         $parsedFrame['languagename'] = $this->LanguageLookup($frame_language, false);
         $parsedFrame['encodingid'] = $frame_textencoding;
         $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding);
         $parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset);
         if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) {
             $ThisFileInfo['id3v2']['comments'][$parsedFrame['framenameshort']][] = getid3_lib::iconv_fallback($parsedFrame['encoding'], $ThisFileInfo['id3v2']['encoding'], $parsedFrame['data']);
         }
         unset($parsedFrame['data']);
     } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'OWNE') {
         // 4.23  OWNE Ownership frame (ID3v2.3+ only)
         //   There may only be one 'OWNE' frame in a tag
         // <Header for 'Ownership frame', ID: 'OWNE'>
         // Text encoding     $xx
         // Price paid        <text string> $00
         // Date of purch.    <text string>
         // Seller            <text string according to encoding>
         $frame_offset = 0;
         $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1));
         if ($id3v2_majorversion <= 3 && $frame_textencoding > 1 || $id3v2_majorversion == 4 && $frame_textencoding > 3) {
             $ThisFileInfo['warning'][] = 'Invalid text encoding byte (' . $frame_textencoding . ') in frame "' . $parsedFrame['frame_name'] . '" - defaulting to ISO-8859-1 encoding';
         }
         $parsedFrame['encodingid'] = $frame_textencoding;
         $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding);
         $frame_terminatorpos = @strpos($parsedFrame['data'], "", $frame_offset);
         $frame_pricepaid = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
         $frame_offset = $frame_terminatorpos + strlen("");
         $parsedFrame['pricepaid']['currencyid'] = substr($frame_pricepaid, 0, 3);
         $parsedFrame['pricepaid']['currency'] = $this->LookupCurrencyUnits($parsedFrame['pricepaid']['currencyid']);
         $parsedFrame['pricepaid']['value'] = substr($frame_pricepaid, 3);
         $parsedFrame['purchasedate'] = substr($parsedFrame['data'], $frame_offset, 8);
         if (!$this->IsValidDateStampString($parsedFrame['purchasedate'])) {
             $parsedFrame['purchasedateunix'] = mktime(0, 0, 0, substr($parsedFrame['purchasedate'], 4, 2), substr($parsedFrame['purchasedate'], 6, 2), substr($parsedFrame['purchasedate'], 0, 4));
         }
         $frame_offset += 8;
         $parsedFrame['seller'] = (string) substr($parsedFrame['data'], $frame_offset);
         unset($parsedFrame['data']);
     } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'COMR') {
         // 4.24  COMR Commercial frame (ID3v2.3+ only)
         //   There may be more than one 'commercial frame' in a tag,
         //   but no two may be identical
         // <Header for 'Commercial frame', ID: 'COMR'>
         // Text encoding      $xx
         // Price string       <text string> $00
         // Valid until        <text string>
         // Contact URL        <text string> $00
         // Received as        $xx
         // Name of seller     <text string according to encoding> $00 (00)
         // Description        <text string according to encoding> $00 (00)
         // Picture MIME type  <string> $00
         // Seller logo        <binary data>
         $frame_offset = 0;
         $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1));
         if ($id3v2_majorversion <= 3 && $frame_textencoding > 1 || $id3v2_majorversion == 4 && $frame_textencoding > 3) {
             $ThisFileInfo['warning'][] = 'Invalid text encoding byte (' . $frame_textencoding . ') in frame "' . $parsedFrame['frame_name'] . '" - defaulting to ISO-8859-1 encoding';
         }
         $frame_terminatorpos = @strpos($parsedFrame['data'], "", $frame_offset);
         $frame_pricestring = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
         $frame_offset = $frame_terminatorpos + strlen("");
         $frame_rawpricearray = explode('/', $frame_pricestring);
         foreach ($frame_rawpricearray as $key => $val) {
             $frame_currencyid = substr($val, 0, 3);
             $parsedFrame['price'][$frame_currencyid]['currency'] = $this->LookupCurrencyUnits($frame_currencyid);
             $parsedFrame['price'][$frame_currencyid]['value'] = substr($val, 3);
         }
         $frame_datestring = substr($parsedFrame['data'], $frame_offset, 8);
         $frame_offset += 8;
         $frame_terminatorpos = @strpos($parsedFrame['data'], "", $frame_offset);
         $frame_contacturl = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
         $frame_offset = $frame_terminatorpos + strlen("");
         $frame_receivedasid = ord(substr($parsedFrame['data'], $frame_offset++, 1));
         $frame_terminatorpos = @strpos($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding), $frame_offset);
         if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) {
             $frame_terminatorpos++;
             // @strpos() fooled because 2nd byte of Unicode chars are often 0x00
         }
         $frame_sellername = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
         if (ord($frame_sellername) === 0) {
             $frame_sellername = '';
         }
         $frame_offset = $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding));
         $frame_terminatorpos = @strpos($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding), $frame_offset);
         if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) {
             $frame_terminatorpos++;
             // @strpos() fooled because 2nd byte of Unicode chars are often 0x00
         }
         $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
         if (ord($frame_description) === 0) {
             $frame_description = '';
         }
         $frame_offset = $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding));
         $frame_terminatorpos = @strpos($parsedFrame['data'], "", $frame_offset);
         $frame_mimetype = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
         $frame_offset = $frame_terminatorpos + strlen("");
         $frame_sellerlogo = substr($parsedFrame['data'], $frame_offset);
         $parsedFrame['encodingid'] = $frame_textencoding;
         $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding);
         $parsedFrame['pricevaliduntil'] = $frame_datestring;
         $parsedFrame['contacturl'] = $frame_contacturl;
         $parsedFrame['receivedasid'] = $frame_receivedasid;
         $parsedFrame['receivedas'] = $this->COMRReceivedAsLookup($frame_receivedasid);
         $parsedFrame['sellername'] = $frame_sellername;
         $parsedFrame['description'] = $frame_description;
         $parsedFrame['mime'] = $frame_mimetype;
         $parsedFrame['logo'] = $frame_sellerlogo;
         unset($parsedFrame['data']);
     } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'ENCR') {
         // 4.25  ENCR Encryption method registration (ID3v2.3+ only)
         //   There may be several 'ENCR' frames in a tag,
         //   but only one containing the same symbol
         //   and only one containing the same owner identifier
         // <Header for 'Encryption method registration', ID: 'ENCR'>
         // Owner identifier    <text string> $00
         // Method symbol       $xx
         // Encryption data     <binary data>
         $frame_offset = 0;
         $frame_terminatorpos = @strpos($parsedFrame['data'], "", $frame_offset);
         $frame_ownerid = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
         if (ord($frame_ownerid) === 0) {
             $frame_ownerid = '';
         }
         $frame_offset = $frame_terminatorpos + strlen("");
         $parsedFrame['ownerid'] = $frame_ownerid;
         $parsedFrame['methodsymbol'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
         $parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset);
     } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'GRID') {
         // 4.26  GRID Group identification registration (ID3v2.3+ only)
         //   There may be several 'GRID' frames in a tag,
         //   but only one containing the same symbol
         //   and only one containing the same owner identifier
         // <Header for 'Group ID registration', ID: 'GRID'>
         // Owner identifier      <text string> $00
         // Group symbol          $xx
         // Group dependent data  <binary data>
         $frame_offset = 0;
         $frame_terminatorpos = @strpos($parsedFrame['data'], "", $frame_offset);
         $frame_ownerid = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
         if (ord($frame_ownerid) === 0) {
             $frame_ownerid = '';
         }
         $frame_offset = $frame_terminatorpos + strlen("");
         $parsedFrame['ownerid'] = $frame_ownerid;
         $parsedFrame['groupsymbol'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
         $parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset);
     } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'PRIV') {
         // 4.27  PRIV Private frame (ID3v2.3+ only)
         //   The tag may contain more than one 'PRIV' frame
         //   but only with different contents
         // <Header for 'Private frame', ID: 'PRIV'>
         // Owner identifier      <text string> $00
         // The private data      <binary data>
         $frame_offset = 0;
         $frame_terminatorpos = @strpos($parsedFrame['data'], "", $frame_offset);
         $frame_ownerid = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
         if (ord($frame_ownerid) === 0) {
             $frame_ownerid = '';
         }
         $frame_offset = $frame_terminatorpos + strlen("");
         $parsedFrame['ownerid'] = $frame_ownerid;
         $parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset);
     } elseif ($id3v2_majorversion >= 4 && $parsedFrame['frame_name'] == 'SIGN') {
         // 4.28  SIGN Signature frame (ID3v2.4+ only)
         //   There may be more than one 'signature frame' in a tag,
         //   but no two may be identical
         // <Header for 'Signature frame', ID: 'SIGN'>
         // Group symbol      $xx
         // Signature         <binary data>
         $frame_offset = 0;
         $parsedFrame['groupsymbol'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
         $parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset);
     } elseif ($id3v2_majorversion >= 4 && $parsedFrame['frame_name'] == 'SEEK') {
         // 4.29  SEEK Seek frame (ID3v2.4+ only)
         //   There may only be one 'seek frame' in a tag
         // <Header for 'Seek frame', ID: 'SEEK'>
         // Minimum offset to next tag       $xx xx xx xx
         $frame_offset = 0;
         $parsedFrame['data'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4));
     } elseif ($id3v2_majorversion >= 4 && $parsedFrame['frame_name'] == 'ASPI') {
         // 4.30  ASPI Audio seek point index (ID3v2.4+ only)
         //   There may only be one 'audio seek point index' frame in a tag
         // <Header for 'Seek Point Index', ID: 'ASPI'>
         // Indexed data start (S)         $xx xx xx xx
         // Indexed data length (L)        $xx xx xx xx
         // Number of index points (N)     $xx xx
         // Bits per index point (b)       $xx
         //   Then for every index point the following data is included:
         // Fraction at index (Fi)          $xx (xx)
         $frame_offset = 0;
         $parsedFrame['datastart'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4));
         $frame_offset += 4;
         $parsedFrame['indexeddatalength'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4));
         $frame_offset += 4;
         $parsedFrame['indexpoints'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 2));
         $frame_offset += 2;
         $parsedFrame['bitsperpoint'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
         $frame_bytesperpoint = ceil($parsedFrame['bitsperpoint'] / 8);
         for ($i = 0; $i < $frame_indexpoints; $i++) {
             $parsedFrame['indexes'][$i] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesperpoint));
             $frame_offset += $frame_bytesperpoint;
         }
         unset($parsedFrame['data']);
     } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'RGAD') {
         // Replay Gain Adjustment
         // http://privatewww.essex.ac.uk/~djmrob/replaygain/file_format_id3v2.html
         //   There may only be one 'RGAD' frame in a tag
         // <Header for 'Replay Gain Adjustment', ID: 'RGAD'>
         // Peak Amplitude                      $xx $xx $xx $xx
         // Radio Replay Gain Adjustment        %aaabbbcd %dddddddd
         // Audiophile Replay Gain Adjustment   %aaabbbcd %dddddddd
         //   a - name code
         //   b - originator code
         //   c - sign bit
         //   d - replay gain adjustment
         $frame_offset = 0;
         $parsedFrame['peakamplitude'] = getid3_lib::BigEndian2Float(substr($parsedFrame['data'], $frame_offset, 4));
         $frame_offset += 4;
         $rg_track_adjustment = getid3_lib::Dec2Bin(substr($parsedFrame['data'], $frame_offset, 2));
         $frame_offset += 2;
         $rg_album_adjustment = getid3_lib::Dec2Bin(substr($parsedFrame['data'], $frame_offset, 2));
         $frame_offset += 2;
         $parsedFrame['raw']['track']['name'] = getid3_lib::Bin2Dec(substr($rg_track_adjustment, 0, 3));
         $parsedFrame['raw']['track']['originator'] = getid3_lib::Bin2Dec(substr($rg_track_adjustment, 3, 3));
         $parsedFrame['raw']['track']['signbit'] = getid3_lib::Bin2Dec(substr($rg_track_adjustment, 6, 1));
         $parsedFrame['raw']['track']['adjustment'] = getid3_lib::Bin2Dec(substr($rg_track_adjustment, 7, 9));
         $parsedFrame['raw']['album']['name'] = getid3_lib::Bin2Dec(substr($rg_album_adjustment, 0, 3));
         $parsedFrame['raw']['album']['originator'] = getid3_lib::Bin2Dec(substr($rg_album_adjustment, 3, 3));
         $parsedFrame['raw']['album']['signbit'] = getid3_lib::Bin2Dec(substr($rg_album_adjustment, 6, 1));
         $parsedFrame['raw']['album']['adjustment'] = getid3_lib::Bin2Dec(substr($rg_album_adjustment, 7, 9));
         $parsedFrame['track']['name'] = getid3_lib::RGADnameLookup($parsedFrame['raw']['track']['name']);
         $parsedFrame['track']['originator'] = getid3_lib::RGADoriginatorLookup($parsedFrame['raw']['track']['originator']);
         $parsedFrame['track']['adjustment'] = getid3_lib::RGADadjustmentLookup($parsedFrame['raw']['track']['adjustment'], $parsedFrame['raw']['track']['signbit']);
         $parsedFrame['album']['name'] = getid3_lib::RGADnameLookup($parsedFrame['raw']['album']['name']);
         $parsedFrame['album']['originator'] = getid3_lib::RGADoriginatorLookup($parsedFrame['raw']['album']['originator']);
         $parsedFrame['album']['adjustment'] = getid3_lib::RGADadjustmentLookup($parsedFrame['raw']['album']['adjustment'], $parsedFrame['raw']['album']['signbit']);
         $ThisFileInfo['replay_gain']['track']['peak'] = $parsedFrame['peakamplitude'];
         $ThisFileInfo['replay_gain']['track']['originator'] = $parsedFrame['track']['originator'];
         $ThisFileInfo['replay_gain']['track']['adjustment'] = $parsedFrame['track']['adjustment'];
         $ThisFileInfo['replay_gain']['album']['originator'] = $parsedFrame['album']['originator'];
         $ThisFileInfo['replay_gain']['album']['adjustment'] = $parsedFrame['album']['adjustment'];
         unset($parsedFrame['data']);
     }
     return true;
 }
Esempio n. 2
0
 function getid3_dts(&$fd, &$ThisFileInfo)
 {
     // Specs taken from "DTS Coherent Acoustics;Core and Extensions,  ETSI TS 102 114 V1.2.1 (2002-12)"
     // (http://pda.etsi.org/pda/queryform.asp)
     // With thanks to Gambit <*****@*****.**> http://mac.sourceforge.net/atl/
     $ThisFileInfo['fileformat'] = 'dts';
     fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
     $DTSheader = fread($fd, 16);
     $ThisFileInfo['dts']['raw']['magic'] = getid3_lib::BigEndian2Int(substr($DTSheader, 0, 4));
     if ($ThisFileInfo['dts']['raw']['magic'] != 0x7ffe8001) {
         $ThisFileInfo['error'][] = 'Expecting "0x7FFE8001" at offset ' . $ThisFileInfo['avdataoffset'] . ', found "0x' . str_pad(strtoupper(dechex($ThisFileInfo['dts']['raw']['magic'])), 8, '0', STR_PAD_LEFT) . '"';
         unset($ThisFileInfo['fileformat']);
         unset($ThisFileInfo['dts']);
         return false;
     }
     $fhBS = getid3_lib::BigEndian2Bin(substr($DTSheader, 4, 12));
     $bsOffset = 0;
     $ThisFileInfo['dts']['raw']['frame_type'] = bindec(substr($fhBS, $bsOffset, 1));
     $bsOffset += 1;
     $ThisFileInfo['dts']['raw']['deficit_samples'] = bindec(substr($fhBS, $bsOffset, 5));
     $bsOffset += 5;
     $ThisFileInfo['dts']['flags']['crc_present'] = (bool) bindec(substr($fhBS, $bsOffset, 1));
     $bsOffset += 1;
     $ThisFileInfo['dts']['raw']['pcm_sample_blocks'] = bindec(substr($fhBS, $bsOffset, 7));
     $bsOffset += 7;
     $ThisFileInfo['dts']['raw']['frame_byte_size'] = bindec(substr($fhBS, $bsOffset, 14));
     $bsOffset += 14;
     $ThisFileInfo['dts']['raw']['channel_arrangement'] = bindec(substr($fhBS, $bsOffset, 6));
     $bsOffset += 6;
     $ThisFileInfo['dts']['raw']['sample_frequency'] = bindec(substr($fhBS, $bsOffset, 4));
     $bsOffset += 4;
     $ThisFileInfo['dts']['raw']['bitrate'] = bindec(substr($fhBS, $bsOffset, 5));
     $bsOffset += 5;
     $ThisFileInfo['dts']['flags']['embedded_downmix'] = (bool) bindec(substr($fhBS, $bsOffset, 1));
     $bsOffset += 1;
     $ThisFileInfo['dts']['flags']['dynamicrange'] = (bool) bindec(substr($fhBS, $bsOffset, 1));
     $bsOffset += 1;
     $ThisFileInfo['dts']['flags']['timestamp'] = (bool) bindec(substr($fhBS, $bsOffset, 1));
     $bsOffset += 1;
     $ThisFileInfo['dts']['flags']['auxdata'] = (bool) bindec(substr($fhBS, $bsOffset, 1));
     $bsOffset += 1;
     $ThisFileInfo['dts']['flags']['hdcd'] = (bool) bindec(substr($fhBS, $bsOffset, 1));
     $bsOffset += 1;
     $ThisFileInfo['dts']['raw']['extension_audio'] = bindec(substr($fhBS, $bsOffset, 3));
     $bsOffset += 3;
     $ThisFileInfo['dts']['flags']['extended_coding'] = (bool) bindec(substr($fhBS, $bsOffset, 1));
     $bsOffset += 1;
     $ThisFileInfo['dts']['flags']['audio_sync_insertion'] = (bool) bindec(substr($fhBS, $bsOffset, 1));
     $bsOffset += 1;
     $ThisFileInfo['dts']['raw']['lfe_effects'] = bindec(substr($fhBS, $bsOffset, 2));
     $bsOffset += 2;
     $ThisFileInfo['dts']['flags']['predictor_history'] = (bool) bindec(substr($fhBS, $bsOffset, 1));
     $bsOffset += 1;
     if ($ThisFileInfo['dts']['flags']['crc_present']) {
         $ThisFileInfo['dts']['raw']['crc16'] = bindec(substr($fhBS, $bsOffset, 16));
         $bsOffset += 16;
     }
     $ThisFileInfo['dts']['flags']['mri_perfect_reconst'] = (bool) bindec(substr($fhBS, $bsOffset, 1));
     $bsOffset += 1;
     $ThisFileInfo['dts']['raw']['encoder_soft_version'] = bindec(substr($fhBS, $bsOffset, 4));
     $bsOffset += 4;
     $ThisFileInfo['dts']['raw']['copy_history'] = bindec(substr($fhBS, $bsOffset, 2));
     $bsOffset += 2;
     $ThisFileInfo['dts']['raw']['bits_per_sample'] = bindec(substr($fhBS, $bsOffset, 2));
     $bsOffset += 2;
     $ThisFileInfo['dts']['flags']['surround_es'] = (bool) bindec(substr($fhBS, $bsOffset, 1));
     $bsOffset += 1;
     $ThisFileInfo['dts']['flags']['front_sum_diff'] = (bool) bindec(substr($fhBS, $bsOffset, 1));
     $bsOffset += 1;
     $ThisFileInfo['dts']['flags']['surround_sum_diff'] = (bool) bindec(substr($fhBS, $bsOffset, 1));
     $bsOffset += 1;
     $ThisFileInfo['dts']['raw']['dialog_normalization'] = bindec(substr($fhBS, $bsOffset, 4));
     $bsOffset += 4;
     $ThisFileInfo['dts']['bitrate'] = $this->DTSbitrateLookup($ThisFileInfo['dts']['raw']['bitrate']);
     $ThisFileInfo['dts']['bits_per_sample'] = $this->DTSbitPerSampleLookup($ThisFileInfo['dts']['raw']['bits_per_sample']);
     $ThisFileInfo['dts']['sample_rate'] = $this->DTSsampleRateLookup($ThisFileInfo['dts']['raw']['sample_frequency']);
     $ThisFileInfo['dts']['dialog_normalization'] = $this->DTSdialogNormalization($ThisFileInfo['dts']['raw']['dialog_normalization'], $ThisFileInfo['dts']['raw']['encoder_soft_version']);
     $ThisFileInfo['dts']['flags']['lossless'] = $ThisFileInfo['dts']['raw']['bitrate'] == 31 ? true : false;
     $ThisFileInfo['dts']['bitrate_mode'] = $ThisFileInfo['dts']['raw']['bitrate'] == 30 ? 'vbr' : 'cbr';
     $ThisFileInfo['dts']['channels'] = $this->DTSnumChannelsLookup($ThisFileInfo['dts']['raw']['channel_arrangement']);
     $ThisFileInfo['dts']['channel_arrangement'] = $this->DTSchannelArrangementLookup($ThisFileInfo['dts']['raw']['channel_arrangement']);
     $ThisFileInfo['audio']['dataformat'] = 'dts';
     $ThisFileInfo['audio']['lossless'] = $ThisFileInfo['dts']['flags']['lossless'];
     $ThisFileInfo['audio']['bitrate_mode'] = $ThisFileInfo['dts']['bitrate_mode'];
     $ThisFileInfo['audio']['bits_per_sample'] = $ThisFileInfo['dts']['bits_per_sample'];
     $ThisFileInfo['audio']['sample_rate'] = $ThisFileInfo['dts']['sample_rate'];
     $ThisFileInfo['audio']['channels'] = $ThisFileInfo['dts']['channels'];
     $ThisFileInfo['audio']['bitrate'] = $ThisFileInfo['dts']['bitrate'];
     if (isset($ThisFileInfo['avdataend'])) {
         $ThisFileInfo['playtime_seconds'] = ($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) / ($ThisFileInfo['dts']['bitrate'] / 8);
     }
     return true;
 }
 public static function FixedPoint2_30($raw_data)
 {
     $binary_string = getid3_lib::BigEndian2Bin($raw_data);
     return bindec(substr($binary_string, 0, 2)) + (double) (bindec(substr($binary_string, 2, 30)) / 1073741824);
     // pow(2, 30) = 1073741824
 }
Esempio n. 4
0
 public function Analyze()
 {
     $getid3 = $this->getid3;
     // http://www.atsc.org/standards/a_52a.pdf
     $getid3->info['fileformat'] = 'ac3';
     $getid3->info['audio']['dataformat'] = 'ac3';
     $getid3->info['audio']['bitrate_mode'] = 'cbr';
     $getid3->info['audio']['lossless'] = false;
     $getid3->info['ac3']['raw']['bsi'] = array();
     $info_ac3 =& $getid3->info['ac3'];
     $info_ac3_raw =& $info_ac3['raw'];
     $info_ac3_raw_bsi =& $info_ac3_raw['bsi'];
     // An AC-3 serial coded audio bit stream is made up of a sequence of synchronization frames
     // Each synchronization frame contains 6 coded audio blocks (AB), each of which represent 256
     // new audio samples per channel. A synchronization information (SI) header at the beginning
     // of each frame contains information needed to acquire and maintain synchronization. A
     // bit stream information (BSI) header follows SI, and contains parameters describing the coded
     // audio service. The coded audio blocks may be followed by an auxiliary data (Aux) field. At the
     // end of each frame is an error check field that includes a CRC word for error detection. An
     // additional CRC word is located in the SI header, the use of which, by a decoder, is optional.
     //
     // syncinfo() | bsi() | AB0 | AB1 | AB2 | AB3 | AB4 | AB5 | Aux | CRC
     $this->fseek($getid3->info['avdataoffset'], SEEK_SET);
     $ac3_header['syncinfo'] = $this->fread(5);
     $info_ac3_raw['synchinfo']['synchword'] = substr($ac3_header['syncinfo'], 0, 2);
     if ($info_ac3_raw['synchinfo']['synchword'] != "\vw") {
         throw new getid3_exception('Expecting "\\x0B\\x77" at offset ' . $getid3->info['avdataoffset'] . ', found \\x' . strtoupper(dechex($ac3_header['syncinfo'][0])) . '\\x' . strtoupper(dechex($ac3_header['syncinfo'][1])) . ' instead');
     }
     // syncinfo() {
     //      syncword    16
     //      crc1        16
     //      fscod        2
     //      frmsizecod   6
     // } /* end of syncinfo */
     $info_ac3_raw['synchinfo']['crc1'] = getid3_lib::LittleEndian2Int(substr($ac3_header['syncinfo'], 2, 2));
     $ac3_synchinfo_fscod_frmsizecod = getid3_lib::LittleEndian2Int(substr($ac3_header['syncinfo'], 4, 1));
     $info_ac3_raw['synchinfo']['fscod'] = ($ac3_synchinfo_fscod_frmsizecod & 0xc0) >> 6;
     $info_ac3_raw['synchinfo']['frmsizecod'] = $ac3_synchinfo_fscod_frmsizecod & 0x3f;
     $info_ac3['sample_rate'] = getid3_ac3::AC3sampleRateCodeLookup($info_ac3_raw['synchinfo']['fscod']);
     if ($info_ac3_raw['synchinfo']['fscod'] <= 3) {
         $getid3->info['audio']['sample_rate'] = $info_ac3['sample_rate'];
     }
     $info_ac3['frame_length'] = getid3_ac3::AC3frameSizeLookup($info_ac3_raw['synchinfo']['frmsizecod'], $info_ac3_raw['synchinfo']['fscod']);
     $info_ac3['bitrate'] = getid3_ac3::AC3bitrateLookup($info_ac3_raw['synchinfo']['frmsizecod']);
     $getid3->info['audio']['bitrate'] = $info_ac3['bitrate'];
     $ac3_header['bsi'] = getid3_lib::BigEndian2Bin($this->fread(15));
     $info_ac3_raw_bsi['bsid'] = bindec(substr($ac3_header['bsi'], 0, 5));
     if ($info_ac3_raw_bsi['bsid'] > 8) {
         // Decoders which can decode version 8 will thus be able to decode version numbers less than 8.
         // If this standard is extended by the addition of additional elements or features, a value of bsid greater than 8 will be used.
         // Decoders built to this version of the standard will not be able to decode versions with bsid greater than 8.
         throw new getid3_exception('Bit stream identification is version ' . $info_ac3_raw_bsi['bsid'] . ', but getID3() only understands up to version 8');
     }
     $info_ac3_raw_bsi['bsmod'] = bindec(substr($ac3_header['bsi'], 5, 3));
     $info_ac3_raw_bsi['acmod'] = bindec(substr($ac3_header['bsi'], 8, 3));
     $info_ac3['service_type'] = getid3_ac3::AC3serviceTypeLookup($info_ac3_raw_bsi['bsmod'], $info_ac3_raw_bsi['acmod']);
     $ac3_coding_mode = getid3_ac3::AC3audioCodingModeLookup($info_ac3_raw_bsi['acmod']);
     foreach ($ac3_coding_mode as $key => $value) {
         $info_ac3[$key] = $value;
     }
     switch ($info_ac3_raw_bsi['acmod']) {
         case 0:
         case 1:
             $getid3->info['audio']['channelmode'] = 'mono';
             break;
         case 3:
         case 4:
             $getid3->info['audio']['channelmode'] = 'stereo';
             break;
         default:
             $getid3->info['audio']['channelmode'] = 'surround';
             break;
     }
     $getid3->info['audio']['channels'] = $info_ac3['num_channels'];
     $offset = 11;
     if ($info_ac3_raw_bsi['acmod'] & 0x1) {
         // If the lsb of acmod is a 1, center channel is in use and cmixlev follows in the bit stream.
         $info_ac3_raw_bsi['cmixlev'] = bindec(substr($ac3_header['bsi'], $offset, 2));
         $info_ac3['center_mix_level'] = getid3_ac3::AC3centerMixLevelLookup($info_ac3_raw_bsi['cmixlev']);
         $offset += 2;
     }
     if ($info_ac3_raw_bsi['acmod'] & 0x4) {
         // If the msb of acmod is a 1, surround channels are in use and surmixlev follows in the bit stream.
         $info_ac3_raw_bsi['surmixlev'] = bindec(substr($ac3_header['bsi'], $offset, 2));
         $info_ac3['surround_mix_level'] = getid3_ac3::AC3surroundMixLevelLookup($info_ac3_raw_bsi['surmixlev']);
         $offset += 2;
     }
     if ($info_ac3_raw_bsi['acmod'] == 0x2) {
         // When operating in the two channel mode, this 2-bit code indicates whether or not the program has been encoded in Dolby Surround.
         $info_ac3_raw_bsi['dsurmod'] = bindec(substr($ac3_header['bsi'], $offset, 2));
         $info_ac3['dolby_surround_mode'] = getid3_ac3::AC3dolbySurroundModeLookup($info_ac3_raw_bsi['dsurmod']);
         $offset += 2;
     }
     $info_ac3_raw_bsi['lfeon'] = $ac3_header['bsi'][$offset++] == '1';
     $info_ac3['lfe_enabled'] = $info_ac3_raw_bsi['lfeon'];
     if ($info_ac3_raw_bsi['lfeon']) {
         $getid3->info['audio']['channels'] .= '.1';
     }
     $info_ac3['channels_enabled'] = getid3_ac3::AC3channelsEnabledLookup($info_ac3_raw_bsi['acmod'], $info_ac3_raw_bsi['lfeon']);
     // This indicates how far the average dialogue level is below digital 100 percent. Valid values are 1–31.
     // The value of 0 is reserved. The values of 1 to 31 are interpreted as -1 dB to -31 dB with respect to digital 100 percent.
     $info_ac3_raw_bsi['dialnorm'] = bindec(substr($ac3_header['bsi'], $offset, 5));
     $offset += 5;
     $info_ac3['dialogue_normalization'] = '-' . $info_ac3_raw_bsi['dialnorm'] . 'dB';
     $info_ac3_raw_bsi['compre_flag'] = $ac3_header['bsi'][$offset++] == '1';
     if ($info_ac3_raw_bsi['compre_flag']) {
         $info_ac3_raw_bsi['compr'] = bindec(substr($ac3_header['bsi'], $offset, 8));
         $offset += 8;
         $info_ac3['heavy_compression'] = getid3_ac3::AC3heavyCompression($info_ac3_raw_bsi['compr']);
     }
     $info_ac3_raw_bsi['langcode_flag'] = $ac3_header['bsi'][$offset++] == '1';
     if ($info_ac3_raw_bsi['langcode_flag']) {
         $info_ac3_raw_bsi['langcod'] = bindec(substr($ac3_header['bsi'], $offset, 8));
         $offset += 8;
     }
     $info_ac3_raw_bsi['audprodie'] = $ac3_header['bsi'][$offset++] == '1';
     if ($info_ac3_raw_bsi['audprodie']) {
         $info_ac3_raw_bsi['mixlevel'] = bindec(substr($ac3_header['bsi'], $offset, 5));
         $offset += 5;
         $info_ac3_raw_bsi['roomtyp'] = bindec(substr($ac3_header['bsi'], $offset, 2));
         $offset += 2;
         $info_ac3['mixing_level'] = 80 + $info_ac3_raw_bsi['mixlevel'] . 'dB';
         $info_ac3['room_type'] = getid3_ac3::AC3roomTypeLookup($info_ac3_raw_bsi['roomtyp']);
     }
     if ($info_ac3_raw_bsi['acmod'] == 0x0) {
         // If acmod is 0, then two completely independent program channels (dual mono)
         // are encoded into the bit stream, and are referenced as Ch1, Ch2. In this case,
         // a number of additional items are present in BSI or audblk to fully describe Ch2.
         // This indicates how far the average dialogue level is below digital 100 percent. Valid values are 1–31.
         // The value of 0 is reserved. The values of 1 to 31 are interpreted as -1 dB to -31 dB with respect to digital 100 percent.
         $info_ac3_raw_bsi['dialnorm2'] = bindec(substr($ac3_header['bsi'], $offset, 5));
         $offset += 5;
         $info_ac3['dialogue_normalization2'] = '-' . $info_ac3_raw_bsi['dialnorm2'] . 'dB';
         $info_ac3_raw_bsi['compre_flag2'] = $ac3_header['bsi'][$offset++] == '1';
         if ($info_ac3_raw_bsi['compre_flag2']) {
             $info_ac3_raw_bsi['compr2'] = bindec(substr($ac3_header['bsi'], $offset, 8));
             $offset += 8;
             $info_ac3['heavy_compression2'] = getid3_ac3::AC3heavyCompression($info_ac3_raw_bsi['compr2']);
         }
         $info_ac3_raw_bsi['langcode_flag2'] = $ac3_header['bsi'][$offset++] == '1';
         if ($info_ac3_raw_bsi['langcode_flag2']) {
             $info_ac3_raw_bsi['langcod2'] = bindec(substr($ac3_header['bsi'], $offset, 8));
             $offset += 8;
         }
         $info_ac3_raw_bsi['audprodie2'] = $ac3_header['bsi'][$offset++] == '1';
         if ($info_ac3_raw_bsi['audprodie2']) {
             $info_ac3_raw_bsi['mixlevel2'] = bindec(substr($ac3_header['bsi'], $offset, 5));
             $offset += 5;
             $info_ac3_raw_bsi['roomtyp2'] = bindec(substr($ac3_header['bsi'], $offset, 2));
             $offset += 2;
             $info_ac3['mixing_level2'] = 80 + $info_ac3_raw_bsi['mixlevel2'] . 'dB';
             $info_ac3['room_type2'] = getid3_ac3::AC3roomTypeLookup($info_ac3_raw_bsi['roomtyp2']);
         }
     }
     $info_ac3_raw_bsi['copyright'] = $ac3_header['bsi'][$offset++] == '1';
     $info_ac3_raw_bsi['original'] = $ac3_header['bsi'][$offset++] == '1';
     $info_ac3_raw_bsi['timecode1_flag'] = $ac3_header['bsi'][$offset++] == '1';
     if ($info_ac3_raw_bsi['timecode1_flag']) {
         $info_ac3_raw_bsi['timecode1'] = bindec(substr($ac3_header['bsi'], $offset, 14));
         $offset += 14;
     }
     $info_ac3_raw_bsi['timecode2_flag'] = $ac3_header['bsi'][$offset++] == '1';
     if ($info_ac3_raw_bsi['timecode2_flag']) {
         $info_ac3_raw_bsi['timecode2'] = bindec(substr($ac3_header['bsi'], $offset, 14));
         $offset += 14;
     }
     $info_ac3_raw_bsi['addbsi_flag'] = $ac3_header['bsi'][$offset++] == '1';
     if ($info_ac3_raw_bsi['addbsi_flag']) {
         $info_ac3_raw_bsi['addbsi_length'] = bindec(substr($ac3_header['bsi'], $offset, 6));
         $offset += 6;
         $ac3_header['bsi'] .= getid3_lib::BigEndian2Bin($this->fread($info_ac3_raw_bsi['addbsi_length']));
         $info_ac3_raw_bsi['addbsi_data'] = substr($ac3_header['bsi'], 119, $info_ac3_raw_bsi['addbsi_length'] * 8);
     }
     return true;
 }
 private function FLACparseSTREAMINFO($meta_data_block_data)
 {
     $getid3 = $this->getid3;
     getid3_lib::ReadSequence('BigEndian2Int', $getid3->info['flac']['STREAMINFO'], $meta_data_block_data, 0, array('min_block_size' => 2, 'max_block_size' => 2, 'min_frame_size' => 3, 'max_frame_size' => 3));
     $sample_rate_channels_sample_bits_stream_samples = getid3_lib::BigEndian2Bin(substr($meta_data_block_data, 10, 8));
     $getid3->info['flac']['STREAMINFO']['sample_rate'] = bindec(substr($sample_rate_channels_sample_bits_stream_samples, 0, 20));
     $getid3->info['flac']['STREAMINFO']['channels'] = bindec(substr($sample_rate_channels_sample_bits_stream_samples, 20, 3)) + 1;
     $getid3->info['flac']['STREAMINFO']['bits_per_sample'] = bindec(substr($sample_rate_channels_sample_bits_stream_samples, 23, 5)) + 1;
     $getid3->info['flac']['STREAMINFO']['samples_stream'] = bindec(substr($sample_rate_channels_sample_bits_stream_samples, 28, 36));
     // bindec() returns float in case of int overrun
     $getid3->info['flac']['STREAMINFO']['audio_signature'] = substr($meta_data_block_data, 18, 16);
     if (!empty($getid3->info['flac']['STREAMINFO']['sample_rate'])) {
         $getid3->info['audio']['bitrate_mode'] = 'vbr';
         $getid3->info['audio']['sample_rate'] = $getid3->info['flac']['STREAMINFO']['sample_rate'];
         $getid3->info['audio']['channels'] = $getid3->info['flac']['STREAMINFO']['channels'];
         $getid3->info['audio']['bits_per_sample'] = $getid3->info['flac']['STREAMINFO']['bits_per_sample'];
         $getid3->info['playtime_seconds'] = $getid3->info['flac']['STREAMINFO']['samples_stream'] / $getid3->info['flac']['STREAMINFO']['sample_rate'];
         $getid3->info['audio']['bitrate'] = ($getid3->info['avdataend'] - $getid3->info['avdataoffset']) * 8 / $getid3->info['playtime_seconds'];
     } else {
         throw new getid3_exception('Corrupt METAdata block: STREAMINFO');
     }
     unset($getid3->info['flac']['STREAMINFO']['raw']);
     return true;
 }
 function getAACADIFheaderFilepointer()
 {
     $info =& $this->getid3->info;
     $info['fileformat'] = 'aac';
     $info['audio']['dataformat'] = 'aac';
     $info['audio']['lossless'] = false;
     fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET);
     $AACheader = fread($this->getid3->fp, 1024);
     $offset = 0;
     if (substr($AACheader, 0, 4) == 'ADIF') {
         // http://faac.sourceforge.net/wiki/index.php?page=ADIF
         // http://libmpeg.org/mpeg4/doc/w2203tfs.pdf
         // adif_header() {
         //     adif_id                                32
         //     copyright_id_present                    1
         //     if( copyright_id_present )
         //         copyright_id                       72
         //     original_copy                           1
         //     home                                    1
         //     bitstream_type                          1
         //     bitrate                                23
         //     num_program_config_elements             4
         //     for (i = 0; i < num_program_config_elements + 1; i++ ) {
         //         if( bitstream_type == '0' )
         //             adif_buffer_fullness           20
         //         program_config_element()
         //     }
         // }
         $AACheaderBitstream = getid3_lib::BigEndian2Bin($AACheader);
         $bitoffset = 0;
         $info['aac']['header_type'] = 'ADIF';
         $bitoffset += 32;
         $info['aac']['header']['mpeg_version'] = 4;
         $info['aac']['header']['copyright'] = (bool) (substr($AACheaderBitstream, $bitoffset, 1) == '1');
         $bitoffset += 1;
         if ($info['aac']['header']['copyright']) {
             $info['aac']['header']['copyright_id'] = getid3_lib::Bin2String(substr($AACheaderBitstream, $bitoffset, 72));
             $bitoffset += 72;
         }
         $info['aac']['header']['original_copy'] = (bool) (substr($AACheaderBitstream, $bitoffset, 1) == '1');
         $bitoffset += 1;
         $info['aac']['header']['home'] = (bool) (substr($AACheaderBitstream, $bitoffset, 1) == '1');
         $bitoffset += 1;
         $info['aac']['header']['is_vbr'] = (bool) (substr($AACheaderBitstream, $bitoffset, 1) == '1');
         $bitoffset += 1;
         if ($info['aac']['header']['is_vbr']) {
             $info['audio']['bitrate_mode'] = 'vbr';
             $info['aac']['header']['bitrate_max'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 23));
             $bitoffset += 23;
         } else {
             $info['audio']['bitrate_mode'] = 'cbr';
             $info['aac']['header']['bitrate'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 23));
             $bitoffset += 23;
             $info['audio']['bitrate'] = $info['aac']['header']['bitrate'];
         }
         if ($info['audio']['bitrate'] == 0) {
             $info['error'][] = 'Corrupt AAC file: bitrate_audio == zero';
             return false;
         }
         $info['aac']['header']['num_program_configs'] = 1 + getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4));
         $bitoffset += 4;
         for ($i = 0; $i < $info['aac']['header']['num_program_configs']; $i++) {
             // http://www.audiocoding.com/wiki/index.php?page=program_config_element
             // buffer_fullness                       20
             // element_instance_tag                   4
             // object_type                            2
             // sampling_frequency_index               4
             // num_front_channel_elements             4
             // num_side_channel_elements              4
             // num_back_channel_elements              4
             // num_lfe_channel_elements               2
             // num_assoc_data_elements                3
             // num_valid_cc_elements                  4
             // mono_mixdown_present                   1
             // mono_mixdown_element_number            4   if mono_mixdown_present == 1
             // stereo_mixdown_present                 1
             // stereo_mixdown_element_number          4   if stereo_mixdown_present == 1
             // matrix_mixdown_idx_present             1
             // matrix_mixdown_idx                     2   if matrix_mixdown_idx_present == 1
             // pseudo_surround_enable                 1   if matrix_mixdown_idx_present == 1
             // for (i = 0; i < num_front_channel_elements; i++) {
             //     front_element_is_cpe[i]            1
             //     front_element_tag_select[i]        4
             // }
             // for (i = 0; i < num_side_channel_elements; i++) {
             //     side_element_is_cpe[i]             1
             //     side_element_tag_select[i]         4
             // }
             // for (i = 0; i < num_back_channel_elements; i++) {
             //     back_element_is_cpe[i]             1
             //     back_element_tag_select[i]         4
             // }
             // for (i = 0; i < num_lfe_channel_elements; i++) {
             //     lfe_element_tag_select[i]          4
             // }
             // for (i = 0; i < num_assoc_data_elements; i++) {
             //     assoc_data_element_tag_select[i]   4
             // }
             // for (i = 0; i < num_valid_cc_elements; i++) {
             //     cc_element_is_ind_sw[i]            1
             //     valid_cc_element_tag_select[i]     4
             // }
             // byte_alignment()                       VAR
             // comment_field_bytes                    8
             // for (i = 0; i < comment_field_bytes; i++) {
             //     comment_field_data[i]              8
             // }
             if (!$info['aac']['header']['is_vbr']) {
                 $info['aac']['program_configs'][$i]['buffer_fullness'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 20));
                 $bitoffset += 20;
             }
             $info['aac']['program_configs'][$i]['element_instance_tag'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4));
             $bitoffset += 4;
             $info['aac']['program_configs'][$i]['object_type'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 2));
             $bitoffset += 2;
             $info['aac']['program_configs'][$i]['sampling_frequency_index'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4));
             $bitoffset += 4;
             $info['aac']['program_configs'][$i]['num_front_channel_elements'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4));
             $bitoffset += 4;
             $info['aac']['program_configs'][$i]['num_side_channel_elements'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4));
             $bitoffset += 4;
             $info['aac']['program_configs'][$i]['num_back_channel_elements'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4));
             $bitoffset += 4;
             $info['aac']['program_configs'][$i]['num_lfe_channel_elements'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 2));
             $bitoffset += 2;
             $info['aac']['program_configs'][$i]['num_assoc_data_elements'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 3));
             $bitoffset += 3;
             $info['aac']['program_configs'][$i]['num_valid_cc_elements'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4));
             $bitoffset += 4;
             $info['aac']['program_configs'][$i]['mono_mixdown_present'] = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1));
             $bitoffset += 1;
             if ($info['aac']['program_configs'][$i]['mono_mixdown_present']) {
                 $info['aac']['program_configs'][$i]['mono_mixdown_element_number'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4));
                 $bitoffset += 4;
             }
             $info['aac']['program_configs'][$i]['stereo_mixdown_present'] = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1));
             $bitoffset += 1;
             if ($info['aac']['program_configs'][$i]['stereo_mixdown_present']) {
                 $info['aac']['program_configs'][$i]['stereo_mixdown_element_number'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4));
                 $bitoffset += 4;
             }
             $info['aac']['program_configs'][$i]['matrix_mixdown_idx_present'] = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1));
             $bitoffset += 1;
             if ($info['aac']['program_configs'][$i]['matrix_mixdown_idx_present']) {
                 $info['aac']['program_configs'][$i]['matrix_mixdown_idx'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 2));
                 $bitoffset += 2;
                 $info['aac']['program_configs'][$i]['pseudo_surround_enable'] = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1));
                 $bitoffset += 1;
             }
             for ($j = 0; $j < $info['aac']['program_configs'][$i]['num_front_channel_elements']; $j++) {
                 $info['aac']['program_configs'][$i]['front_element_is_cpe'][$j] = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1));
                 $bitoffset += 1;
                 $info['aac']['program_configs'][$i]['front_element_tag_select'][$j] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4));
                 $bitoffset += 4;
             }
             for ($j = 0; $j < $info['aac']['program_configs'][$i]['num_side_channel_elements']; $j++) {
                 $info['aac']['program_configs'][$i]['side_element_is_cpe'][$j] = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1));
                 $bitoffset += 1;
                 $info['aac']['program_configs'][$i]['side_element_tag_select'][$j] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4));
                 $bitoffset += 4;
             }
             for ($j = 0; $j < $info['aac']['program_configs'][$i]['num_back_channel_elements']; $j++) {
                 $info['aac']['program_configs'][$i]['back_element_is_cpe'][$j] = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1));
                 $bitoffset += 1;
                 $info['aac']['program_configs'][$i]['back_element_tag_select'][$j] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4));
                 $bitoffset += 4;
             }
             for ($j = 0; $j < $info['aac']['program_configs'][$i]['num_lfe_channel_elements']; $j++) {
                 $info['aac']['program_configs'][$i]['lfe_element_tag_select'][$j] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4));
                 $bitoffset += 4;
             }
             for ($j = 0; $j < $info['aac']['program_configs'][$i]['num_assoc_data_elements']; $j++) {
                 $info['aac']['program_configs'][$i]['assoc_data_element_tag_select'][$j] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4));
                 $bitoffset += 4;
             }
             for ($j = 0; $j < $info['aac']['program_configs'][$i]['num_valid_cc_elements']; $j++) {
                 $info['aac']['program_configs'][$i]['cc_element_is_ind_sw'][$j] = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1));
                 $bitoffset += 1;
                 $info['aac']['program_configs'][$i]['valid_cc_element_tag_select'][$j] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4));
                 $bitoffset += 4;
             }
             $bitoffset = ceil($bitoffset / 8) * 8;
             $info['aac']['program_configs'][$i]['comment_field_bytes'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 8));
             $bitoffset += 8;
             $info['aac']['program_configs'][$i]['comment_field'] = getid3_lib::Bin2String(substr($AACheaderBitstream, $bitoffset, 8 * $info['aac']['program_configs'][$i]['comment_field_bytes']));
             $bitoffset += 8 * $info['aac']['program_configs'][$i]['comment_field_bytes'];
             $info['aac']['header']['profile'] = self::AACprofileLookup($info['aac']['program_configs'][$i]['object_type'], $info['aac']['header']['mpeg_version']);
             $info['aac']['program_configs'][$i]['sampling_frequency'] = self::AACsampleRateLookup($info['aac']['program_configs'][$i]['sampling_frequency_index']);
             $info['audio']['sample_rate'] = $info['aac']['program_configs'][$i]['sampling_frequency'];
             $info['audio']['channels'] = self::AACchannelCountCalculate($info['aac']['program_configs'][$i]);
             if ($info['aac']['program_configs'][$i]['comment_field']) {
                 $info['aac']['comments'][] = $info['aac']['program_configs'][$i]['comment_field'];
             }
         }
         $info['playtime_seconds'] = ($info['avdataend'] - $info['avdataoffset']) * 8 / $info['audio']['bitrate'];
         $info['audio']['encoder_options'] = $info['aac']['header_type'] . ' ' . $info['aac']['header']['profile'];
         return true;
     } else {
         unset($info['fileformat']);
         unset($info['aac']);
         $info['error'][] = 'AAC-ADIF synch not found at offset ' . $info['avdataoffset'] . ' (expected "ADIF", found "' . substr($AACheader, 0, 4) . '" instead)';
         return false;
     }
 }
Esempio n. 7
0
 public static function BigEndian2Float($byte_word)
 {
     // ANSI/IEEE Standard 754-1985, Standard for Binary Floating Point Arithmetic
     // http://www.psc.edu/general/software/packages/ieee/ieee.html
     // http://www.scri.fsu.edu/~jac/MAD3401/Backgrnd/ieee.html
     $bit_word = getid3_lib::BigEndian2Bin($byte_word);
     if (!$bit_word) {
         return 0;
     }
     $sign_bit = $bit_word[0];
     switch (strlen($byte_word) * 8) {
         case 32:
             $exponent_bits = 8;
             $fraction_bits = 23;
             break;
         case 64:
             $exponent_bits = 11;
             $fraction_bits = 52;
             break;
         case 80:
             // 80-bit Apple SANE format
             // http://www.mactech.com/articles/mactech/Vol.06/06.01/SANENormalized/
             $exponent_string = substr($bit_word, 1, 15);
             $is_normalized = intval($bit_word[16]);
             $fraction_string = substr($bit_word, 17, 63);
             $exponent = pow(2, getid3_lib::Bin2Dec($exponent_string) - 16383);
             $fraction = $is_normalized + getid3_lib::DecimalBinary2Float($fraction_string);
             $float_value = $exponent * $fraction;
             if ($sign_bit == '1') {
                 $float_value *= -1;
             }
             return $float_value;
             break;
         default:
             return false;
             break;
     }
     $exponent_string = substr($bit_word, 1, $exponent_bits);
     $fraction_string = substr($bit_word, $exponent_bits + 1, $fraction_bits);
     $exponent = bindec($exponent_string);
     $fraction = bindec($fraction_string);
     if ($exponent == pow(2, $exponent_bits) - 1 && $fraction != 0) {
         // Not a Number
         $float_value = false;
     } elseif ($exponent == pow(2, $exponent_bits) - 1 && $fraction == 0) {
         if ($sign_bit == '1') {
             $float_value = '-infinity';
         } else {
             $float_value = '+infinity';
         }
     } elseif ($exponent == 0 && $fraction == 0) {
         if ($sign_bit == '1') {
             $float_value = -0;
         } else {
             $float_value = 0;
         }
         $float_value = $sign_bit ? 0 : -0;
     } elseif ($exponent == 0 && $fraction != 0) {
         // These are 'unnormalized' values
         $float_value = pow(2, -1 * (pow(2, $exponent_bits - 1) - 2)) * getid3_lib::DecimalBinary2Float($fraction_string);
         if ($sign_bit == '1') {
             $float_value *= -1;
         }
     } elseif ($exponent != 0) {
         $float_value = pow(2, $exponent - (pow(2, $exponent_bits - 1) - 1)) * (1 + getid3_lib::DecimalBinary2Float($fraction_string));
         if ($sign_bit == '1') {
             $float_value *= -1;
         }
     }
     return (double) $float_value;
 }
Esempio n. 8
0
 public function Analyze()
 {
     $info =& $this->getid3->info;
     // Specs taken from "DTS Coherent Acoustics;Core and Extensions,  ETSI TS 102 114 V1.2.1 (2002-12)"
     // (http://pda.etsi.org/pda/queryform.asp)
     // With thanks to Gambit <*****@*****.**> http://mac.sourceforge.net/atl/
     $info['fileformat'] = 'dts';
     $this->fseek($info['avdataoffset']);
     $DTSheader = $this->fread(16);
     if (strpos($DTSheader, self::syncword) === 0) {
         $info['dts']['raw']['magic'] = self::syncword;
         $offset = 4;
     } else {
         if (!$this->isDependencyFor('matroska')) {
             unset($info['fileformat']);
             return $this->error('Expecting "' . getid3_lib::PrintHexBytes(self::syncword) . '" at offset ' . $info['avdataoffset'] . ', found "' . getid3_lib::PrintHexBytes(substr($DTSheader, 0, 4)) . '"');
         }
         $offset = 0;
     }
     $fhBS = getid3_lib::BigEndian2Bin(substr($DTSheader, $offset, 12));
     $bsOffset = 0;
     $info['dts']['raw']['frame_type'] = $this->readBinData($fhBS, $bsOffset, 1);
     $info['dts']['raw']['deficit_samples'] = $this->readBinData($fhBS, $bsOffset, 5);
     $info['dts']['flags']['crc_present'] = (bool) $this->readBinData($fhBS, $bsOffset, 1);
     $info['dts']['raw']['pcm_sample_blocks'] = $this->readBinData($fhBS, $bsOffset, 7);
     $info['dts']['raw']['frame_byte_size'] = $this->readBinData($fhBS, $bsOffset, 14);
     $info['dts']['raw']['channel_arrangement'] = $this->readBinData($fhBS, $bsOffset, 6);
     $info['dts']['raw']['sample_frequency'] = $this->readBinData($fhBS, $bsOffset, 4);
     $info['dts']['raw']['bitrate'] = $this->readBinData($fhBS, $bsOffset, 5);
     $info['dts']['flags']['embedded_downmix'] = (bool) $this->readBinData($fhBS, $bsOffset, 1);
     $info['dts']['flags']['dynamicrange'] = (bool) $this->readBinData($fhBS, $bsOffset, 1);
     $info['dts']['flags']['timestamp'] = (bool) $this->readBinData($fhBS, $bsOffset, 1);
     $info['dts']['flags']['auxdata'] = (bool) $this->readBinData($fhBS, $bsOffset, 1);
     $info['dts']['flags']['hdcd'] = (bool) $this->readBinData($fhBS, $bsOffset, 1);
     $info['dts']['raw']['extension_audio'] = $this->readBinData($fhBS, $bsOffset, 3);
     $info['dts']['flags']['extended_coding'] = (bool) $this->readBinData($fhBS, $bsOffset, 1);
     $info['dts']['flags']['audio_sync_insertion'] = (bool) $this->readBinData($fhBS, $bsOffset, 1);
     $info['dts']['raw']['lfe_effects'] = $this->readBinData($fhBS, $bsOffset, 2);
     $info['dts']['flags']['predictor_history'] = (bool) $this->readBinData($fhBS, $bsOffset, 1);
     if ($info['dts']['flags']['crc_present']) {
         $info['dts']['raw']['crc16'] = $this->readBinData($fhBS, $bsOffset, 16);
     }
     $info['dts']['flags']['mri_perfect_reconst'] = (bool) $this->readBinData($fhBS, $bsOffset, 1);
     $info['dts']['raw']['encoder_soft_version'] = $this->readBinData($fhBS, $bsOffset, 4);
     $info['dts']['raw']['copy_history'] = $this->readBinData($fhBS, $bsOffset, 2);
     $info['dts']['raw']['bits_per_sample'] = $this->readBinData($fhBS, $bsOffset, 2);
     $info['dts']['flags']['surround_es'] = (bool) $this->readBinData($fhBS, $bsOffset, 1);
     $info['dts']['flags']['front_sum_diff'] = (bool) $this->readBinData($fhBS, $bsOffset, 1);
     $info['dts']['flags']['surround_sum_diff'] = (bool) $this->readBinData($fhBS, $bsOffset, 1);
     $info['dts']['raw']['dialog_normalization'] = $this->readBinData($fhBS, $bsOffset, 4);
     $info['dts']['bitrate'] = self::bitrateLookup($info['dts']['raw']['bitrate']);
     $info['dts']['bits_per_sample'] = self::bitPerSampleLookup($info['dts']['raw']['bits_per_sample']);
     $info['dts']['sample_rate'] = self::sampleRateLookup($info['dts']['raw']['sample_frequency']);
     $info['dts']['dialog_normalization'] = self::dialogNormalization($info['dts']['raw']['dialog_normalization'], $info['dts']['raw']['encoder_soft_version']);
     $info['dts']['flags']['lossless'] = $info['dts']['raw']['bitrate'] == 31 ? true : false;
     $info['dts']['bitrate_mode'] = $info['dts']['raw']['bitrate'] == 30 ? 'vbr' : 'cbr';
     $info['dts']['channels'] = self::numChannelsLookup($info['dts']['raw']['channel_arrangement']);
     $info['dts']['channel_arrangement'] = self::channelArrangementLookup($info['dts']['raw']['channel_arrangement']);
     $info['audio']['dataformat'] = 'dts';
     $info['audio']['lossless'] = $info['dts']['flags']['lossless'];
     $info['audio']['bitrate_mode'] = $info['dts']['bitrate_mode'];
     $info['audio']['bits_per_sample'] = $info['dts']['bits_per_sample'];
     $info['audio']['sample_rate'] = $info['dts']['sample_rate'];
     $info['audio']['channels'] = $info['dts']['channels'];
     $info['audio']['bitrate'] = $info['dts']['bitrate'];
     if (isset($info['avdataend'])) {
         $info['playtime_seconds'] = ($info['avdataend'] - $info['avdataoffset']) / ($info['dts']['bitrate'] / 8);
     }
     return true;
 }
Esempio n. 9
0
 public function ParseID3v2Frame(&$parsedFrame)
 {
     // shortcuts
     $info =& $this->getid3->info;
     $id3v2_majorversion = $info['id3v2']['majorversion'];
     $parsedFrame['framenamelong'] = $this->FrameNameLongLookup($parsedFrame['frame_name']);
     if (empty($parsedFrame['framenamelong'])) {
         unset($parsedFrame['framenamelong']);
     }
     $parsedFrame['framenameshort'] = $this->FrameNameShortLookup($parsedFrame['frame_name']);
     if (empty($parsedFrame['framenameshort'])) {
         unset($parsedFrame['framenameshort']);
     }
     if ($id3v2_majorversion >= 3) {
         // frame flags are not part of the ID3v2.2 standard
         if ($id3v2_majorversion == 3) {
             //    Frame Header Flags
             //    %abc00000 %ijk00000
             $parsedFrame['flags']['TagAlterPreservation'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x8000);
             // a - Tag alter preservation
             $parsedFrame['flags']['FileAlterPreservation'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x4000);
             // b - File alter preservation
             $parsedFrame['flags']['ReadOnly'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x2000);
             // c - Read only
             $parsedFrame['flags']['compression'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x80);
             // i - Compression
             $parsedFrame['flags']['Encryption'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x40);
             // j - Encryption
             $parsedFrame['flags']['GroupingIdentity'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x20);
             // k - Grouping identity
         } elseif ($id3v2_majorversion == 4) {
             //    Frame Header Flags
             //    %0abc0000 %0h00kmnp
             $parsedFrame['flags']['TagAlterPreservation'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x4000);
             // a - Tag alter preservation
             $parsedFrame['flags']['FileAlterPreservation'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x2000);
             // b - File alter preservation
             $parsedFrame['flags']['ReadOnly'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x1000);
             // c - Read only
             $parsedFrame['flags']['GroupingIdentity'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x40);
             // h - Grouping identity
             $parsedFrame['flags']['compression'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x8);
             // k - Compression
             $parsedFrame['flags']['Encryption'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x4);
             // m - Encryption
             $parsedFrame['flags']['Unsynchronisation'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x2);
             // n - Unsynchronisation
             $parsedFrame['flags']['DataLengthIndicator'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x1);
             // p - Data length indicator
             // Frame-level de-unsynchronisation - ID3v2.4
             if ($parsedFrame['flags']['Unsynchronisation']) {
                 $parsedFrame['data'] = $this->DeUnsynchronise($parsedFrame['data']);
             }
             if ($parsedFrame['flags']['DataLengthIndicator']) {
                 $parsedFrame['data_length_indicator'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], 0, 4), 1);
                 $parsedFrame['data'] = substr($parsedFrame['data'], 4);
             }
         }
         //    Frame-level de-compression
         if ($parsedFrame['flags']['compression']) {
             $parsedFrame['decompressed_size'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], 0, 4));
             if (!function_exists('gzuncompress')) {
                 $info['warning'][] = 'gzuncompress() support required to decompress ID3v2 frame "' . $parsedFrame['frame_name'] . '"';
             } else {
                 if ($decompresseddata = @gzuncompress(substr($parsedFrame['data'], 4))) {
                     //if ($decompresseddata = @gzuncompress($parsedFrame['data'])) {
                     $parsedFrame['data'] = $decompresseddata;
                     unset($decompresseddata);
                 } else {
                     $info['warning'][] = 'gzuncompress() failed on compressed contents of ID3v2 frame "' . $parsedFrame['frame_name'] . '"';
                 }
             }
         }
     }
     if (!empty($parsedFrame['flags']['DataLengthIndicator'])) {
         if ($parsedFrame['data_length_indicator'] != strlen($parsedFrame['data'])) {
             $info['warning'][] = 'ID3v2 frame "' . $parsedFrame['frame_name'] . '" should be ' . $parsedFrame['data_length_indicator'] . ' bytes long according to DataLengthIndicator, but found ' . strlen($parsedFrame['data']) . ' bytes of data';
         }
     }
     if (isset($parsedFrame['datalength']) && $parsedFrame['datalength'] == 0) {
         $warning = 'Frame "' . $parsedFrame['frame_name'] . '" at offset ' . $parsedFrame['dataoffset'] . ' has no data portion';
         switch ($parsedFrame['frame_name']) {
             case 'WCOM':
                 $warning .= ' (this is known to happen with files tagged by RioPort)';
                 break;
             default:
                 break;
         }
         $info['warning'][] = $warning;
     } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'UFID' || $id3v2_majorversion == 2 && $parsedFrame['frame_name'] == 'UFI') {
         // 4.1   UFI  Unique file identifier
         //   There may be more than one 'UFID' frame in a tag,
         //   but only one with the same 'Owner identifier'.
         // <Header for 'Unique file identifier', ID: 'UFID'>
         // Owner identifier        <text string> $00
         // Identifier              <up to 64 bytes binary data>
         $exploded = explode("", $parsedFrame['data'], 2);
         $parsedFrame['ownerid'] = isset($exploded[0]) ? $exploded[0] : '';
         $parsedFrame['data'] = isset($exploded[1]) ? $exploded[1] : '';
     } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'TXXX' || $id3v2_majorversion == 2 && $parsedFrame['frame_name'] == 'TXX') {
         // 4.2.2 TXX  User defined text information frame
         //   There may be more than one 'TXXX' frame in each tag,
         //   but only one with the same description.
         // <Header for 'User defined text information frame', ID: 'TXXX'>
         // Text encoding     $xx
         // Description       <text string according to encoding> $00 (00)
         // Value             <text string according to encoding>
         $frame_offset = 0;
         $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1));
         $frame_textencoding_terminator = $this->TextEncodingTerminatorLookup($frame_textencoding);
         if ($id3v2_majorversion <= 3 && $frame_textencoding > 1 || $id3v2_majorversion == 4 && $frame_textencoding > 3) {
             $info['warning'][] = 'Invalid text encoding byte (' . $frame_textencoding . ') in frame "' . $parsedFrame['frame_name'] . '" - defaulting to ISO-8859-1 encoding';
             $frame_textencoding_terminator = "";
         }
         $frame_terminatorpos = strpos($parsedFrame['data'], $frame_textencoding_terminator, $frame_offset);
         if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator), 1)) === 0) {
             $frame_terminatorpos++;
             // strpos() fooled because 2nd byte of Unicode chars are often 0x00
         }
         $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
         if (ord($frame_description) === 0) {
             $frame_description = '';
         }
         $parsedFrame['encodingid'] = $frame_textencoding;
         $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding);
         $parsedFrame['description'] = trim(getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $frame_description));
         $parsedFrame['data'] = substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator));
         if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) {
             $commentkey = $parsedFrame['description'] ? $parsedFrame['description'] : (isset($info['id3v2']['comments'][$parsedFrame['framenameshort']]) ? count($info['id3v2']['comments'][$parsedFrame['framenameshort']]) : 0);
             if (!isset($info['id3v2']['comments'][$parsedFrame['framenameshort']]) || !array_key_exists($commentkey, $info['id3v2']['comments'][$parsedFrame['framenameshort']])) {
                 $info['id3v2']['comments'][$parsedFrame['framenameshort']][$commentkey] = trim(getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $parsedFrame['data']));
             } else {
                 $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = trim(getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $parsedFrame['data']));
             }
         }
         //unset($parsedFrame['data']); do not unset, may be needed elsewhere, e.g. for replaygain
     } elseif ($parsedFrame['frame_name'][0] == 'T') {
         // 4.2. T??[?] Text information frame
         //   There may only be one text information frame of its kind in an tag.
         // <Header for 'Text information frame', ID: 'T000' - 'TZZZ',
         // excluding 'TXXX' described in 4.2.6.>
         // Text encoding                $xx
         // Information                  <text string(s) according to encoding>
         $frame_offset = 0;
         $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1));
         if ($id3v2_majorversion <= 3 && $frame_textencoding > 1 || $id3v2_majorversion == 4 && $frame_textencoding > 3) {
             $info['warning'][] = 'Invalid text encoding byte (' . $frame_textencoding . ') in frame "' . $parsedFrame['frame_name'] . '" - defaulting to ISO-8859-1 encoding';
         }
         $parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset);
         $parsedFrame['encodingid'] = $frame_textencoding;
         $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding);
         if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) {
             // ID3v2.3 specs say that TPE1 (and others) can contain multiple artist values separated with /
             // This of course breaks when an artist name contains slash character, e.g. "AC/DC"
             // MP3tag (maybe others) implement alternative system where multiple artists are null-separated, which makes more sense
             // getID3 will split null-separated artists into multiple artists and leave slash-separated ones to the user
             switch ($parsedFrame['encoding']) {
                 case 'UTF-16':
                 case 'UTF-16BE':
                 case 'UTF-16LE':
                     $wordsize = 2;
                     break;
                 case 'ISO-8859-1':
                 case 'UTF-8':
                 default:
                     $wordsize = 1;
                     break;
             }
             $Txxx_elements = array();
             $Txxx_elements_start_offset = 0;
             for ($i = 0; $i < strlen($parsedFrame['data']); $i += $wordsize) {
                 if (substr($parsedFrame['data'], $i, $wordsize) == str_repeat("", $wordsize)) {
                     $Txxx_elements[] = substr($parsedFrame['data'], $Txxx_elements_start_offset, $i - $Txxx_elements_start_offset);
                     $Txxx_elements_start_offset = $i + $wordsize;
                 }
             }
             $Txxx_elements[] = substr($parsedFrame['data'], $Txxx_elements_start_offset, $i - $Txxx_elements_start_offset);
             foreach ($Txxx_elements as $Txxx_element) {
                 $string = getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $Txxx_element);
                 if (!empty($string)) {
                     $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = $string;
                 }
             }
             unset($string, $wordsize, $i, $Txxx_elements, $Txxx_element, $Txxx_elements_start_offset);
         }
     } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'WXXX' || $id3v2_majorversion == 2 && $parsedFrame['frame_name'] == 'WXX') {
         // 4.3.2 WXX  User defined URL link frame
         //   There may be more than one 'WXXX' frame in each tag,
         //   but only one with the same description
         // <Header for 'User defined URL link frame', ID: 'WXXX'>
         // Text encoding     $xx
         // Description       <text string according to encoding> $00 (00)
         // URL               <text string>
         $frame_offset = 0;
         $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1));
         $frame_textencoding_terminator = $this->TextEncodingTerminatorLookup($frame_textencoding);
         if ($id3v2_majorversion <= 3 && $frame_textencoding > 1 || $id3v2_majorversion == 4 && $frame_textencoding > 3) {
             $info['warning'][] = 'Invalid text encoding byte (' . $frame_textencoding . ') in frame "' . $parsedFrame['frame_name'] . '" - defaulting to ISO-8859-1 encoding';
             $frame_textencoding_terminator = "";
         }
         $frame_terminatorpos = strpos($parsedFrame['data'], $frame_textencoding_terminator, $frame_offset);
         if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator), 1)) === 0) {
             $frame_terminatorpos++;
             // strpos() fooled because 2nd byte of Unicode chars are often 0x00
         }
         $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
         if (ord($frame_description) === 0) {
             $frame_description = '';
         }
         $parsedFrame['data'] = substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator));
         $frame_terminatorpos = strpos($parsedFrame['data'], $frame_textencoding_terminator);
         if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator), 1)) === 0) {
             $frame_terminatorpos++;
             // strpos() fooled because 2nd byte of Unicode chars are often 0x00
         }
         if ($frame_terminatorpos) {
             // there are null bytes after the data - this is not according to spec
             // only use data up to first null byte
             $frame_urldata = (string) substr($parsedFrame['data'], 0, $frame_terminatorpos);
         } else {
             // no null bytes following data, just use all data
             $frame_urldata = (string) $parsedFrame['data'];
         }
         $parsedFrame['encodingid'] = $frame_textencoding;
         $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding);
         $parsedFrame['url'] = $frame_urldata;
         $parsedFrame['description'] = $frame_description;
         if (!empty($parsedFrame['framenameshort']) && $parsedFrame['url']) {
             $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $parsedFrame['url']);
         }
         unset($parsedFrame['data']);
     } elseif ($parsedFrame['frame_name'][0] == 'W') {
         // 4.3. W??? URL link frames
         //   There may only be one URL link frame of its kind in a tag,
         //   except when stated otherwise in the frame description
         // <Header for 'URL link frame', ID: 'W000' - 'WZZZ', excluding 'WXXX'
         // described in 4.3.2.>
         // URL              <text string>
         $parsedFrame['url'] = trim($parsedFrame['data']);
         if (!empty($parsedFrame['framenameshort']) && $parsedFrame['url']) {
             $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = $parsedFrame['url'];
         }
         unset($parsedFrame['data']);
     } elseif ($id3v2_majorversion == 3 && $parsedFrame['frame_name'] == 'IPLS' || $id3v2_majorversion == 2 && $parsedFrame['frame_name'] == 'IPL') {
         // 4.4  IPL  Involved people list (ID3v2.2 only)
         // http://id3.org/id3v2.3.0#sec4.4
         //   There may only be one 'IPL' frame in each tag
         // <Header for 'User defined URL link frame', ID: 'IPL'>
         // Text encoding     $xx
         // People list strings    <textstrings>
         $frame_offset = 0;
         $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1));
         if ($id3v2_majorversion <= 3 && $frame_textencoding > 1 || $id3v2_majorversion == 4 && $frame_textencoding > 3) {
             $info['warning'][] = 'Invalid text encoding byte (' . $frame_textencoding . ') in frame "' . $parsedFrame['frame_name'] . '" - defaulting to ISO-8859-1 encoding';
         }
         $parsedFrame['encodingid'] = $frame_textencoding;
         $parsedFrame['encoding'] = $this->TextEncodingNameLookup($parsedFrame['encodingid']);
         $parsedFrame['data_raw'] = (string) substr($parsedFrame['data'], $frame_offset);
         // http://www.getid3.org/phpBB3/viewtopic.php?t=1369
         // "this tag typically contains null terminated strings, which are associated in pairs"
         // "there are users that use the tag incorrectly"
         $IPLS_parts = array();
         if (strpos($parsedFrame['data_raw'], "") !== false) {
             $IPLS_parts_unsorted = array();
             if (strlen($parsedFrame['data_raw']) % 2 == 0 && (substr($parsedFrame['data_raw'], 0, 2) == "ÿþ" || substr($parsedFrame['data_raw'], 0, 2) == "þÿ")) {
                 // UTF-16, be careful looking for null bytes since most 2-byte characters may contain one; you need to find twin null bytes, and on even padding
                 $thisILPS = '';
                 for ($i = 0; $i < strlen($parsedFrame['data_raw']); $i += 2) {
                     $twobytes = substr($parsedFrame['data_raw'], $i, 2);
                     if ($twobytes === "") {
                         $IPLS_parts_unsorted[] = getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $thisILPS);
                         $thisILPS = '';
                     } else {
                         $thisILPS .= $twobytes;
                     }
                 }
                 if (strlen($thisILPS) > 2) {
                     // 2-byte BOM
                     $IPLS_parts_unsorted[] = getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $thisILPS);
                 }
             } else {
                 // ISO-8859-1 or UTF-8 or other single-byte-null character set
                 $IPLS_parts_unsorted = explode("", $parsedFrame['data_raw']);
             }
             if (count($IPLS_parts_unsorted) == 1) {
                 // just a list of names, e.g. "Dino Baptiste, Jimmy Copley, John Gordon, Bernie Marsden, Sharon Watson"
                 foreach ($IPLS_parts_unsorted as $key => $value) {
                     $IPLS_parts_sorted = preg_split('#[;,\\r\\n\\t]#', $value);
                     $position = '';
                     foreach ($IPLS_parts_sorted as $person) {
                         $IPLS_parts[] = array('position' => $position, 'person' => $person);
                     }
                 }
             } elseif (count($IPLS_parts_unsorted) % 2 == 0) {
                 $position = '';
                 $person = '';
                 foreach ($IPLS_parts_unsorted as $key => $value) {
                     if ($key % 2 == 0) {
                         $position = $value;
                     } else {
                         $person = $value;
                         $IPLS_parts[] = array('position' => $position, 'person' => $person);
                         $position = '';
                         $person = '';
                     }
                 }
             } else {
                 foreach ($IPLS_parts_unsorted as $key => $value) {
                     $IPLS_parts[] = array($value);
                 }
             }
         } else {
             $IPLS_parts = preg_split('#[;,\\r\\n\\t]#', $parsedFrame['data_raw']);
         }
         $parsedFrame['data'] = $IPLS_parts;
         if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) {
             $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = $parsedFrame['data'];
         }
     } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'MCDI' || $id3v2_majorversion == 2 && $parsedFrame['frame_name'] == 'MCI') {
         // 4.5   MCI  Music CD identifier
         //   There may only be one 'MCDI' frame in each tag
         // <Header for 'Music CD identifier', ID: 'MCDI'>
         // CD TOC                <binary data>
         if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) {
             $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = $parsedFrame['data'];
         }
     } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'ETCO' || $id3v2_majorversion == 2 && $parsedFrame['frame_name'] == 'ETC') {
         // 4.6   ETC  Event timing codes
         //   There may only be one 'ETCO' frame in each tag
         // <Header for 'Event timing codes', ID: 'ETCO'>
         // Time stamp format    $xx
         //   Where time stamp format is:
         // $01  (32-bit value) MPEG frames from beginning of file
         // $02  (32-bit value) milliseconds from beginning of file
         //   Followed by a list of key events in the following format:
         // Type of event   $xx
         // Time stamp      $xx (xx ...)
         //   The 'Time stamp' is set to zero if directly at the beginning of the sound
         //   or after the previous event. All events MUST be sorted in chronological order.
         $frame_offset = 0;
         $parsedFrame['timestampformat'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
         while ($frame_offset < strlen($parsedFrame['data'])) {
             $parsedFrame['typeid'] = substr($parsedFrame['data'], $frame_offset++, 1);
             $parsedFrame['type'] = $this->ETCOEventLookup($parsedFrame['typeid']);
             $parsedFrame['timestamp'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4));
             $frame_offset += 4;
         }
         unset($parsedFrame['data']);
     } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'MLLT' || $id3v2_majorversion == 2 && $parsedFrame['frame_name'] == 'MLL') {
         // 4.7   MLL MPEG location lookup table
         //   There may only be one 'MLLT' frame in each tag
         // <Header for 'Location lookup table', ID: 'MLLT'>
         // MPEG frames between reference  $xx xx
         // Bytes between reference        $xx xx xx
         // Milliseconds between reference $xx xx xx
         // Bits for bytes deviation       $xx
         // Bits for milliseconds dev.     $xx
         //   Then for every reference the following data is included;
         // Deviation in bytes         %xxx....
         // Deviation in milliseconds  %xxx....
         $frame_offset = 0;
         $parsedFrame['framesbetweenreferences'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], 0, 2));
         $parsedFrame['bytesbetweenreferences'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], 2, 3));
         $parsedFrame['msbetweenreferences'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], 5, 3));
         $parsedFrame['bitsforbytesdeviation'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], 8, 1));
         $parsedFrame['bitsformsdeviation'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], 9, 1));
         $parsedFrame['data'] = substr($parsedFrame['data'], 10);
         while ($frame_offset < strlen($parsedFrame['data'])) {
             $deviationbitstream .= getid3_lib::BigEndian2Bin(substr($parsedFrame['data'], $frame_offset++, 1));
         }
         $reference_counter = 0;
         while (strlen($deviationbitstream) > 0) {
             $parsedFrame[$reference_counter]['bytedeviation'] = bindec(substr($deviationbitstream, 0, $parsedFrame['bitsforbytesdeviation']));
             $parsedFrame[$reference_counter]['msdeviation'] = bindec(substr($deviationbitstream, $parsedFrame['bitsforbytesdeviation'], $parsedFrame['bitsformsdeviation']));
             $deviationbitstream = substr($deviationbitstream, $parsedFrame['bitsforbytesdeviation'] + $parsedFrame['bitsformsdeviation']);
             $reference_counter++;
         }
         unset($parsedFrame['data']);
     } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'SYTC' || $id3v2_majorversion == 2 && $parsedFrame['frame_name'] == 'STC') {
         // 4.8   STC  Synchronised tempo codes
         //   There may only be one 'SYTC' frame in each tag
         // <Header for 'Synchronised tempo codes', ID: 'SYTC'>
         // Time stamp format   $xx
         // Tempo data          <binary data>
         //   Where time stamp format is:
         // $01  (32-bit value) MPEG frames from beginning of file
         // $02  (32-bit value) milliseconds from beginning of file
         $frame_offset = 0;
         $parsedFrame['timestampformat'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
         $timestamp_counter = 0;
         while ($frame_offset < strlen($parsedFrame['data'])) {
             $parsedFrame[$timestamp_counter]['tempo'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
             if ($parsedFrame[$timestamp_counter]['tempo'] == 255) {
                 $parsedFrame[$timestamp_counter]['tempo'] += ord(substr($parsedFrame['data'], $frame_offset++, 1));
             }
             $parsedFrame[$timestamp_counter]['timestamp'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4));
             $frame_offset += 4;
             $timestamp_counter++;
         }
         unset($parsedFrame['data']);
     } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'USLT' || $id3v2_majorversion == 2 && $parsedFrame['frame_name'] == 'ULT') {
         // 4.9   ULT  Unsynchronised lyric/text transcription
         //   There may be more than one 'Unsynchronised lyrics/text transcription' frame
         //   in each tag, but only one with the same language and content descriptor.
         // <Header for 'Unsynchronised lyrics/text transcription', ID: 'USLT'>
         // Text encoding        $xx
         // Language             $xx xx xx
         // Content descriptor   <text string according to encoding> $00 (00)
         // Lyrics/text          <full text string according to encoding>
         $frame_offset = 0;
         $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1));
         $frame_textencoding_terminator = $this->TextEncodingTerminatorLookup($frame_textencoding);
         if ($id3v2_majorversion <= 3 && $frame_textencoding > 1 || $id3v2_majorversion == 4 && $frame_textencoding > 3) {
             $info['warning'][] = 'Invalid text encoding byte (' . $frame_textencoding . ') in frame "' . $parsedFrame['frame_name'] . '" - defaulting to ISO-8859-1 encoding';
             $frame_textencoding_terminator = "";
         }
         $frame_language = substr($parsedFrame['data'], $frame_offset, 3);
         $frame_offset += 3;
         $frame_terminatorpos = strpos($parsedFrame['data'], $frame_textencoding_terminator, $frame_offset);
         if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator), 1)) === 0) {
             $frame_terminatorpos++;
             // strpos() fooled because 2nd byte of Unicode chars are often 0x00
         }
         $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
         if (ord($frame_description) === 0) {
             $frame_description = '';
         }
         $parsedFrame['data'] = substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator));
         $parsedFrame['encodingid'] = $frame_textencoding;
         $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding);
         $parsedFrame['data'] = $parsedFrame['data'];
         $parsedFrame['language'] = $frame_language;
         $parsedFrame['languagename'] = $this->LanguageLookup($frame_language, false);
         $parsedFrame['description'] = $frame_description;
         if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) {
             $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $parsedFrame['data']);
         }
         unset($parsedFrame['data']);
     } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'SYLT' || $id3v2_majorversion == 2 && $parsedFrame['frame_name'] == 'SLT') {
         // 4.10  SLT  Synchronised lyric/text
         //   There may be more than one 'SYLT' frame in each tag,
         //   but only one with the same language and content descriptor.
         // <Header for 'Synchronised lyrics/text', ID: 'SYLT'>
         // Text encoding        $xx
         // Language             $xx xx xx
         // Time stamp format    $xx
         //   $01  (32-bit value) MPEG frames from beginning of file
         //   $02  (32-bit value) milliseconds from beginning of file
         // Content type         $xx
         // Content descriptor   <text string according to encoding> $00 (00)
         //   Terminated text to be synced (typically a syllable)
         //   Sync identifier (terminator to above string)   $00 (00)
         //   Time stamp                                     $xx (xx ...)
         $frame_offset = 0;
         $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1));
         $frame_textencoding_terminator = $this->TextEncodingTerminatorLookup($frame_textencoding);
         if ($id3v2_majorversion <= 3 && $frame_textencoding > 1 || $id3v2_majorversion == 4 && $frame_textencoding > 3) {
             $info['warning'][] = 'Invalid text encoding byte (' . $frame_textencoding . ') in frame "' . $parsedFrame['frame_name'] . '" - defaulting to ISO-8859-1 encoding';
             $frame_textencoding_terminator = "";
         }
         $frame_language = substr($parsedFrame['data'], $frame_offset, 3);
         $frame_offset += 3;
         $parsedFrame['timestampformat'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
         $parsedFrame['contenttypeid'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
         $parsedFrame['contenttype'] = $this->SYTLContentTypeLookup($parsedFrame['contenttypeid']);
         $parsedFrame['encodingid'] = $frame_textencoding;
         $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding);
         $parsedFrame['language'] = $frame_language;
         $parsedFrame['languagename'] = $this->LanguageLookup($frame_language, false);
         $timestampindex = 0;
         $frame_remainingdata = substr($parsedFrame['data'], $frame_offset);
         while (strlen($frame_remainingdata)) {
             $frame_offset = 0;
             $frame_terminatorpos = strpos($frame_remainingdata, $frame_textencoding_terminator);
             if ($frame_terminatorpos === false) {
                 $frame_remainingdata = '';
             } else {
                 if (ord(substr($frame_remainingdata, $frame_terminatorpos + strlen($frame_textencoding_terminator), 1)) === 0) {
                     $frame_terminatorpos++;
                     // strpos() fooled because 2nd byte of Unicode chars are often 0x00
                 }
                 $parsedFrame['lyrics'][$timestampindex]['data'] = substr($frame_remainingdata, $frame_offset, $frame_terminatorpos - $frame_offset);
                 $frame_remainingdata = substr($frame_remainingdata, $frame_terminatorpos + strlen($frame_textencoding_terminator));
                 if ($timestampindex == 0 && ord($frame_remainingdata[0]) != 0) {
                     // timestamp probably omitted for first data item
                 } else {
                     $parsedFrame['lyrics'][$timestampindex]['timestamp'] = getid3_lib::BigEndian2Int(substr($frame_remainingdata, 0, 4));
                     $frame_remainingdata = substr($frame_remainingdata, 4);
                 }
                 $timestampindex++;
             }
         }
         unset($parsedFrame['data']);
     } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'COMM' || $id3v2_majorversion == 2 && $parsedFrame['frame_name'] == 'COM') {
         // 4.11  COM  Comments
         //   There may be more than one comment frame in each tag,
         //   but only one with the same language and content descriptor.
         // <Header for 'Comment', ID: 'COMM'>
         // Text encoding          $xx
         // Language               $xx xx xx
         // Short content descrip. <text string according to encoding> $00 (00)
         // The actual text        <full text string according to encoding>
         if (strlen($parsedFrame['data']) < 5) {
             $info['warning'][] = 'Invalid data (too short) for "' . $parsedFrame['frame_name'] . '" frame at offset ' . $parsedFrame['dataoffset'];
         } else {
             $frame_offset = 0;
             $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1));
             $frame_textencoding_terminator = $this->TextEncodingTerminatorLookup($frame_textencoding);
             if ($id3v2_majorversion <= 3 && $frame_textencoding > 1 || $id3v2_majorversion == 4 && $frame_textencoding > 3) {
                 $info['warning'][] = 'Invalid text encoding byte (' . $frame_textencoding . ') in frame "' . $parsedFrame['frame_name'] . '" - defaulting to ISO-8859-1 encoding';
                 $frame_textencoding_terminator = "";
             }
             $frame_language = substr($parsedFrame['data'], $frame_offset, 3);
             $frame_offset += 3;
             $frame_terminatorpos = strpos($parsedFrame['data'], $frame_textencoding_terminator, $frame_offset);
             if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator), 1)) === 0) {
                 $frame_terminatorpos++;
                 // strpos() fooled because 2nd byte of Unicode chars are often 0x00
             }
             $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
             if (ord($frame_description) === 0) {
                 $frame_description = '';
             }
             $frame_text = (string) substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator));
             $parsedFrame['encodingid'] = $frame_textencoding;
             $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding);
             $parsedFrame['language'] = $frame_language;
             $parsedFrame['languagename'] = $this->LanguageLookup($frame_language, false);
             $parsedFrame['description'] = $frame_description;
             $parsedFrame['data'] = $frame_text;
             if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) {
                 $commentkey = $parsedFrame['description'] ? $parsedFrame['description'] : (!empty($info['id3v2']['comments'][$parsedFrame['framenameshort']]) ? count($info['id3v2']['comments'][$parsedFrame['framenameshort']]) : 0);
                 if (!isset($info['id3v2']['comments'][$parsedFrame['framenameshort']]) || !array_key_exists($commentkey, $info['id3v2']['comments'][$parsedFrame['framenameshort']])) {
                     $info['id3v2']['comments'][$parsedFrame['framenameshort']][$commentkey] = getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $parsedFrame['data']);
                 } else {
                     $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $parsedFrame['data']);
                 }
             }
         }
     } elseif ($id3v2_majorversion >= 4 && $parsedFrame['frame_name'] == 'RVA2') {
         // 4.11  RVA2 Relative volume adjustment (2) (ID3v2.4+ only)
         //   There may be more than one 'RVA2' frame in each tag,
         //   but only one with the same identification string
         // <Header for 'Relative volume adjustment (2)', ID: 'RVA2'>
         // Identification          <text string> $00
         //   The 'identification' string is used to identify the situation and/or
         //   device where this adjustment should apply. The following is then
         //   repeated for every channel:
         // Type of channel         $xx
         // Volume adjustment       $xx xx
         // Bits representing peak  $xx
         // Peak volume             $xx (xx ...)
         $frame_terminatorpos = strpos($parsedFrame['data'], "");
         $frame_idstring = substr($parsedFrame['data'], 0, $frame_terminatorpos);
         if (ord($frame_idstring) === 0) {
             $frame_idstring = '';
         }
         $frame_remainingdata = substr($parsedFrame['data'], $frame_terminatorpos + strlen(""));
         $parsedFrame['description'] = $frame_idstring;
         $RVA2channelcounter = 0;
         while (strlen($frame_remainingdata) >= 5) {
             $frame_offset = 0;
             $frame_channeltypeid = ord(substr($frame_remainingdata, $frame_offset++, 1));
             $parsedFrame[$RVA2channelcounter]['channeltypeid'] = $frame_channeltypeid;
             $parsedFrame[$RVA2channelcounter]['channeltype'] = $this->RVA2ChannelTypeLookup($frame_channeltypeid);
             $parsedFrame[$RVA2channelcounter]['volumeadjust'] = getid3_lib::BigEndian2Int(substr($frame_remainingdata, $frame_offset, 2), false, true);
             // 16-bit signed
             $frame_offset += 2;
             $parsedFrame[$RVA2channelcounter]['bitspeakvolume'] = ord(substr($frame_remainingdata, $frame_offset++, 1));
             if ($parsedFrame[$RVA2channelcounter]['bitspeakvolume'] < 1 || $parsedFrame[$RVA2channelcounter]['bitspeakvolume'] > 4) {
                 $info['warning'][] = 'ID3v2::RVA2 frame[' . $RVA2channelcounter . '] contains invalid ' . $parsedFrame[$RVA2channelcounter]['bitspeakvolume'] . '-byte bits-representing-peak value';
                 break;
             }
             $frame_bytespeakvolume = ceil($parsedFrame[$RVA2channelcounter]['bitspeakvolume'] / 8);
             $parsedFrame[$RVA2channelcounter]['peakvolume'] = getid3_lib::BigEndian2Int(substr($frame_remainingdata, $frame_offset, $frame_bytespeakvolume));
             $frame_remainingdata = substr($frame_remainingdata, $frame_offset + $frame_bytespeakvolume);
             $RVA2channelcounter++;
         }
         unset($parsedFrame['data']);
     } elseif ($id3v2_majorversion == 3 && $parsedFrame['frame_name'] == 'RVAD' || $id3v2_majorversion == 2 && $parsedFrame['frame_name'] == 'RVA') {
         // 4.12  RVA  Relative volume adjustment (ID3v2.2 only)
         //   There may only be one 'RVA' frame in each tag
         // <Header for 'Relative volume adjustment', ID: 'RVA'>
         // ID3v2.2 => Increment/decrement     %000000ba
         // ID3v2.3 => Increment/decrement     %00fedcba
         // Bits used for volume descr.        $xx
         // Relative volume change, right      $xx xx (xx ...) // a
         // Relative volume change, left       $xx xx (xx ...) // b
         // Peak volume right                  $xx xx (xx ...)
         // Peak volume left                   $xx xx (xx ...)
         //   ID3v2.3 only, optional (not present in ID3v2.2):
         // Relative volume change, right back $xx xx (xx ...) // c
         // Relative volume change, left back  $xx xx (xx ...) // d
         // Peak volume right back             $xx xx (xx ...)
         // Peak volume left back              $xx xx (xx ...)
         //   ID3v2.3 only, optional (not present in ID3v2.2):
         // Relative volume change, center     $xx xx (xx ...) // e
         // Peak volume center                 $xx xx (xx ...)
         //   ID3v2.3 only, optional (not present in ID3v2.2):
         // Relative volume change, bass       $xx xx (xx ...) // f
         // Peak volume bass                   $xx xx (xx ...)
         $frame_offset = 0;
         $frame_incrdecrflags = getid3_lib::BigEndian2Bin(substr($parsedFrame['data'], $frame_offset++, 1));
         $parsedFrame['incdec']['right'] = (bool) substr($frame_incrdecrflags, 6, 1);
         $parsedFrame['incdec']['left'] = (bool) substr($frame_incrdecrflags, 7, 1);
         $parsedFrame['bitsvolume'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
         $frame_bytesvolume = ceil($parsedFrame['bitsvolume'] / 8);
         $parsedFrame['volumechange']['right'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume));
         if ($parsedFrame['incdec']['right'] === false) {
             $parsedFrame['volumechange']['right'] *= -1;
         }
         $frame_offset += $frame_bytesvolume;
         $parsedFrame['volumechange']['left'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume));
         if ($parsedFrame['incdec']['left'] === false) {
             $parsedFrame['volumechange']['left'] *= -1;
         }
         $frame_offset += $frame_bytesvolume;
         $parsedFrame['peakvolume']['right'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume));
         $frame_offset += $frame_bytesvolume;
         $parsedFrame['peakvolume']['left'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume));
         $frame_offset += $frame_bytesvolume;
         if ($id3v2_majorversion == 3) {
             $parsedFrame['data'] = substr($parsedFrame['data'], $frame_offset);
             if (strlen($parsedFrame['data']) > 0) {
                 $parsedFrame['incdec']['rightrear'] = (bool) substr($frame_incrdecrflags, 4, 1);
                 $parsedFrame['incdec']['leftrear'] = (bool) substr($frame_incrdecrflags, 5, 1);
                 $parsedFrame['volumechange']['rightrear'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume));
                 if ($parsedFrame['incdec']['rightrear'] === false) {
                     $parsedFrame['volumechange']['rightrear'] *= -1;
                 }
                 $frame_offset += $frame_bytesvolume;
                 $parsedFrame['volumechange']['leftrear'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume));
                 if ($parsedFrame['incdec']['leftrear'] === false) {
                     $parsedFrame['volumechange']['leftrear'] *= -1;
                 }
                 $frame_offset += $frame_bytesvolume;
                 $parsedFrame['peakvolume']['rightrear'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume));
                 $frame_offset += $frame_bytesvolume;
                 $parsedFrame['peakvolume']['leftrear'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume));
                 $frame_offset += $frame_bytesvolume;
             }
             $parsedFrame['data'] = substr($parsedFrame['data'], $frame_offset);
             if (strlen($parsedFrame['data']) > 0) {
                 $parsedFrame['incdec']['center'] = (bool) substr($frame_incrdecrflags, 3, 1);
                 $parsedFrame['volumechange']['center'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume));
                 if ($parsedFrame['incdec']['center'] === false) {
                     $parsedFrame['volumechange']['center'] *= -1;
                 }
                 $frame_offset += $frame_bytesvolume;
                 $parsedFrame['peakvolume']['center'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume));
                 $frame_offset += $frame_bytesvolume;
             }
             $parsedFrame['data'] = substr($parsedFrame['data'], $frame_offset);
             if (strlen($parsedFrame['data']) > 0) {
                 $parsedFrame['incdec']['bass'] = (bool) substr($frame_incrdecrflags, 2, 1);
                 $parsedFrame['volumechange']['bass'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume));
                 if ($parsedFrame['incdec']['bass'] === false) {
                     $parsedFrame['volumechange']['bass'] *= -1;
                 }
                 $frame_offset += $frame_bytesvolume;
                 $parsedFrame['peakvolume']['bass'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume));
                 $frame_offset += $frame_bytesvolume;
             }
         }
         unset($parsedFrame['data']);
     } elseif ($id3v2_majorversion >= 4 && $parsedFrame['frame_name'] == 'EQU2') {
         // 4.12  EQU2 Equalisation (2) (ID3v2.4+ only)
         //   There may be more than one 'EQU2' frame in each tag,
         //   but only one with the same identification string
         // <Header of 'Equalisation (2)', ID: 'EQU2'>
         // Interpolation method  $xx
         //   $00  Band
         //   $01  Linear
         // Identification        <text string> $00
         //   The following is then repeated for every adjustment point
         // Frequency          $xx xx
         // Volume adjustment  $xx xx
         $frame_offset = 0;
         $frame_interpolationmethod = ord(substr($parsedFrame['data'], $frame_offset++, 1));
         $frame_terminatorpos = strpos($parsedFrame['data'], "", $frame_offset);
         $frame_idstring = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
         if (ord($frame_idstring) === 0) {
             $frame_idstring = '';
         }
         $parsedFrame['description'] = $frame_idstring;
         $frame_remainingdata = substr($parsedFrame['data'], $frame_terminatorpos + strlen(""));
         while (strlen($frame_remainingdata)) {
             $frame_frequency = getid3_lib::BigEndian2Int(substr($frame_remainingdata, 0, 2)) / 2;
             $parsedFrame['data'][$frame_frequency] = getid3_lib::BigEndian2Int(substr($frame_remainingdata, 2, 2), false, true);
             $frame_remainingdata = substr($frame_remainingdata, 4);
         }
         $parsedFrame['interpolationmethod'] = $frame_interpolationmethod;
         unset($parsedFrame['data']);
     } elseif ($id3v2_majorversion == 3 && $parsedFrame['frame_name'] == 'EQUA' || $id3v2_majorversion == 2 && $parsedFrame['frame_name'] == 'EQU') {
         // 4.13  EQU  Equalisation (ID3v2.2 only)
         //   There may only be one 'EQUA' frame in each tag
         // <Header for 'Relative volume adjustment', ID: 'EQU'>
         // Adjustment bits    $xx
         //   This is followed by 2 bytes + ('adjustment bits' rounded up to the
         //   nearest byte) for every equalisation band in the following format,
         //   giving a frequency range of 0 - 32767Hz:
         // Increment/decrement   %x (MSB of the Frequency)
         // Frequency             (lower 15 bits)
         // Adjustment            $xx (xx ...)
         $frame_offset = 0;
         $parsedFrame['adjustmentbits'] = substr($parsedFrame['data'], $frame_offset++, 1);
         $frame_adjustmentbytes = ceil($parsedFrame['adjustmentbits'] / 8);
         $frame_remainingdata = (string) substr($parsedFrame['data'], $frame_offset);
         while (strlen($frame_remainingdata) > 0) {
             $frame_frequencystr = getid3_lib::BigEndian2Bin(substr($frame_remainingdata, 0, 2));
             $frame_incdec = (bool) substr($frame_frequencystr, 0, 1);
             $frame_frequency = bindec(substr($frame_frequencystr, 1, 15));
             $parsedFrame[$frame_frequency]['incdec'] = $frame_incdec;
             $parsedFrame[$frame_frequency]['adjustment'] = getid3_lib::BigEndian2Int(substr($frame_remainingdata, 2, $frame_adjustmentbytes));
             if ($parsedFrame[$frame_frequency]['incdec'] === false) {
                 $parsedFrame[$frame_frequency]['adjustment'] *= -1;
             }
             $frame_remainingdata = substr($frame_remainingdata, 2 + $frame_adjustmentbytes);
         }
         unset($parsedFrame['data']);
     } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'RVRB' || $id3v2_majorversion == 2 && $parsedFrame['frame_name'] == 'REV') {
         // 4.14  REV  Reverb
         //   There may only be one 'RVRB' frame in each tag.
         // <Header for 'Reverb', ID: 'RVRB'>
         // Reverb left (ms)                 $xx xx
         // Reverb right (ms)                $xx xx
         // Reverb bounces, left             $xx
         // Reverb bounces, right            $xx
         // Reverb feedback, left to left    $xx
         // Reverb feedback, left to right   $xx
         // Reverb feedback, right to right  $xx
         // Reverb feedback, right to left   $xx
         // Premix left to right             $xx
         // Premix right to left             $xx
         $frame_offset = 0;
         $parsedFrame['left'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 2));
         $frame_offset += 2;
         $parsedFrame['right'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 2));
         $frame_offset += 2;
         $parsedFrame['bouncesL'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
         $parsedFrame['bouncesR'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
         $parsedFrame['feedbackLL'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
         $parsedFrame['feedbackLR'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
         $parsedFrame['feedbackRR'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
         $parsedFrame['feedbackRL'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
         $parsedFrame['premixLR'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
         $parsedFrame['premixRL'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
         unset($parsedFrame['data']);
     } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'APIC' || $id3v2_majorversion == 2 && $parsedFrame['frame_name'] == 'PIC') {
         // 4.15  PIC  Attached picture
         //   There may be several pictures attached to one file,
         //   each in their individual 'APIC' frame, but only one
         //   with the same content descriptor
         // <Header for 'Attached picture', ID: 'APIC'>
         // Text encoding      $xx
         // ID3v2.3+ => MIME type          <text string> $00
         // ID3v2.2  => Image format       $xx xx xx
         // Picture type       $xx
         // Description        <text string according to encoding> $00 (00)
         // Picture data       <binary data>
         $frame_offset = 0;
         $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1));
         $frame_textencoding_terminator = $this->TextEncodingTerminatorLookup($frame_textencoding);
         if ($id3v2_majorversion <= 3 && $frame_textencoding > 1 || $id3v2_majorversion == 4 && $frame_textencoding > 3) {
             $info['warning'][] = 'Invalid text encoding byte (' . $frame_textencoding . ') in frame "' . $parsedFrame['frame_name'] . '" - defaulting to ISO-8859-1 encoding';
             $frame_textencoding_terminator = "";
         }
         if ($id3v2_majorversion == 2 && strlen($parsedFrame['data']) > $frame_offset) {
             $frame_imagetype = substr($parsedFrame['data'], $frame_offset, 3);
             if (strtolower($frame_imagetype) == 'ima') {
                 // complete hack for mp3Rage (www.chaoticsoftware.com) that puts ID3v2.3-formatted
                 // MIME type instead of 3-char ID3v2.2-format image type  (thanks xbhoffØpacbell*net)
                 $frame_terminatorpos = strpos($parsedFrame['data'], "", $frame_offset);
                 $frame_mimetype = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
                 if (ord($frame_mimetype) === 0) {
                     $frame_mimetype = '';
                 }
                 $frame_imagetype = strtoupper(str_replace('image/', '', strtolower($frame_mimetype)));
                 if ($frame_imagetype == 'JPEG') {
                     $frame_imagetype = 'JPG';
                 }
                 $frame_offset = $frame_terminatorpos + strlen("");
             } else {
                 $frame_offset += 3;
             }
         }
         if ($id3v2_majorversion > 2 && strlen($parsedFrame['data']) > $frame_offset) {
             $frame_terminatorpos = strpos($parsedFrame['data'], "", $frame_offset);
             $frame_mimetype = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
             if (ord($frame_mimetype) === 0) {
                 $frame_mimetype = '';
             }
             $frame_offset = $frame_terminatorpos + strlen("");
         }
         $frame_picturetype = ord(substr($parsedFrame['data'], $frame_offset++, 1));
         if ($frame_offset >= $parsedFrame['datalength']) {
             $info['warning'][] = 'data portion of APIC frame is missing at offset ' . ($parsedFrame['dataoffset'] + 8 + $frame_offset);
         } else {
             $frame_terminatorpos = strpos($parsedFrame['data'], $frame_textencoding_terminator, $frame_offset);
             if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator), 1)) === 0) {
                 $frame_terminatorpos++;
                 // strpos() fooled because 2nd byte of Unicode chars are often 0x00
             }
             $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
             if (ord($frame_description) === 0) {
                 $frame_description = '';
             }
             $parsedFrame['encodingid'] = $frame_textencoding;
             $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding);
             if ($id3v2_majorversion == 2) {
                 $parsedFrame['imagetype'] = $frame_imagetype;
             } else {
                 $parsedFrame['mime'] = $frame_mimetype;
             }
             $parsedFrame['picturetypeid'] = $frame_picturetype;
             $parsedFrame['picturetype'] = $this->APICPictureTypeLookup($frame_picturetype);
             $parsedFrame['description'] = $frame_description;
             $parsedFrame['data'] = substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator));
             $parsedFrame['datalength'] = strlen($parsedFrame['data']);
             $parsedFrame['image_mime'] = '';
             $imageinfo = array();
             $imagechunkcheck = getid3_lib::GetDataImageSize($parsedFrame['data'], $imageinfo);
             if ($imagechunkcheck[2] >= 1 && $imagechunkcheck[2] <= 3) {
                 $parsedFrame['image_mime'] = 'image/' . getid3_lib::ImageTypesLookup($imagechunkcheck[2]);
                 if ($imagechunkcheck[0]) {
                     $parsedFrame['image_width'] = $imagechunkcheck[0];
                 }
                 if ($imagechunkcheck[1]) {
                     $parsedFrame['image_height'] = $imagechunkcheck[1];
                 }
             }
             do {
                 if ($this->getid3->option_save_attachments === false) {
                     // skip entirely
                     unset($parsedFrame['data']);
                     break;
                 }
                 if ($this->getid3->option_save_attachments === true) {
                     // great
                     /*
                     					} elseif (is_int($this->getid3->option_save_attachments)) {
                     						if ($this->getid3->option_save_attachments < $parsedFrame['data_length']) {
                     							// too big, skip
                     							$info['warning'][] = 'attachment at '.$frame_offset.' is too large to process inline ('.number_format($parsedFrame['data_length']).' bytes)';
                     							unset($parsedFrame['data']);
                     							break;
                     						}
                     */
                 } elseif (is_string($this->getid3->option_save_attachments)) {
                     $dir = rtrim(str_replace(array('/', '\\'), DIRECTORY_SEPARATOR, $this->getid3->option_save_attachments), DIRECTORY_SEPARATOR);
                     if (!is_dir($dir) || !is_writable($dir)) {
                         // cannot write, skip
                         $info['warning'][] = 'attachment at ' . $frame_offset . ' cannot be saved to "' . $dir . '" (not writable)';
                         unset($parsedFrame['data']);
                         break;
                     }
                 }
                 // if we get this far, must be OK
                 if (is_string($this->getid3->option_save_attachments)) {
                     $destination_filename = $dir . DIRECTORY_SEPARATOR . md5($info['filenamepath']) . '_' . $frame_offset;
                     if (!file_exists($destination_filename) || is_writable($destination_filename)) {
                         file_put_contents($destination_filename, $parsedFrame['data']);
                     } else {
                         $info['warning'][] = 'attachment at ' . $frame_offset . ' cannot be saved to "' . $destination_filename . '" (not writable)';
                     }
                     $parsedFrame['data_filename'] = $destination_filename;
                     unset($parsedFrame['data']);
                 } else {
                     if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) {
                         if (!isset($info['id3v2']['comments']['picture'])) {
                             $info['id3v2']['comments']['picture'] = array();
                         }
                         $comments_picture_data = array();
                         foreach (array('data', 'image_mime', 'image_width', 'image_height', 'imagetype', 'picturetype', 'description', 'datalength') as $picture_key) {
                             if (isset($parsedFrame[$picture_key])) {
                                 $comments_picture_data[$picture_key] = $parsedFrame[$picture_key];
                             }
                         }
                         $info['id3v2']['comments']['picture'][] = $comments_picture_data;
                         unset($comments_picture_data);
                     }
                 }
             } while (false);
         }
     } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'GEOB' || $id3v2_majorversion == 2 && $parsedFrame['frame_name'] == 'GEO') {
         // 4.16  GEO  General encapsulated object
         //   There may be more than one 'GEOB' frame in each tag,
         //   but only one with the same content descriptor
         // <Header for 'General encapsulated object', ID: 'GEOB'>
         // Text encoding          $xx
         // MIME type              <text string> $00
         // Filename               <text string according to encoding> $00 (00)
         // Content description    <text string according to encoding> $00 (00)
         // Encapsulated object    <binary data>
         $frame_offset = 0;
         $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1));
         $frame_textencoding_terminator = $this->TextEncodingTerminatorLookup($frame_textencoding);
         if ($id3v2_majorversion <= 3 && $frame_textencoding > 1 || $id3v2_majorversion == 4 && $frame_textencoding > 3) {
             $info['warning'][] = 'Invalid text encoding byte (' . $frame_textencoding . ') in frame "' . $parsedFrame['frame_name'] . '" - defaulting to ISO-8859-1 encoding';
             $frame_textencoding_terminator = "";
         }
         $frame_terminatorpos = strpos($parsedFrame['data'], "", $frame_offset);
         $frame_mimetype = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
         if (ord($frame_mimetype) === 0) {
             $frame_mimetype = '';
         }
         $frame_offset = $frame_terminatorpos + strlen("");
         $frame_terminatorpos = strpos($parsedFrame['data'], $frame_textencoding_terminator, $frame_offset);
         if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator), 1)) === 0) {
             $frame_terminatorpos++;
             // strpos() fooled because 2nd byte of Unicode chars are often 0x00
         }
         $frame_filename = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
         if (ord($frame_filename) === 0) {
             $frame_filename = '';
         }
         $frame_offset = $frame_terminatorpos + strlen($frame_textencoding_terminator);
         $frame_terminatorpos = strpos($parsedFrame['data'], $frame_textencoding_terminator, $frame_offset);
         if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator), 1)) === 0) {
             $frame_terminatorpos++;
             // strpos() fooled because 2nd byte of Unicode chars are often 0x00
         }
         $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
         if (ord($frame_description) === 0) {
             $frame_description = '';
         }
         $frame_offset = $frame_terminatorpos + strlen($frame_textencoding_terminator);
         $parsedFrame['objectdata'] = (string) substr($parsedFrame['data'], $frame_offset);
         $parsedFrame['encodingid'] = $frame_textencoding;
         $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding);
         $parsedFrame['mime'] = $frame_mimetype;
         $parsedFrame['filename'] = $frame_filename;
         $parsedFrame['description'] = $frame_description;
         unset($parsedFrame['data']);
     } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'PCNT' || $id3v2_majorversion == 2 && $parsedFrame['frame_name'] == 'CNT') {
         // 4.17  CNT  Play counter
         //   There may only be one 'PCNT' frame in each tag.
         //   When the counter reaches all one's, one byte is inserted in
         //   front of the counter thus making the counter eight bits bigger
         // <Header for 'Play counter', ID: 'PCNT'>
         // Counter        $xx xx xx xx (xx ...)
         $parsedFrame['data'] = getid3_lib::BigEndian2Int($parsedFrame['data']);
     } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'POPM' || $id3v2_majorversion == 2 && $parsedFrame['frame_name'] == 'POP') {
         // 4.18  POP  Popularimeter
         //   There may be more than one 'POPM' frame in each tag,
         //   but only one with the same email address
         // <Header for 'Popularimeter', ID: 'POPM'>
         // Email to user   <text string> $00
         // Rating          $xx
         // Counter         $xx xx xx xx (xx ...)
         $frame_offset = 0;
         $frame_terminatorpos = strpos($parsedFrame['data'], "", $frame_offset);
         $frame_emailaddress = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
         if (ord($frame_emailaddress) === 0) {
             $frame_emailaddress = '';
         }
         $frame_offset = $frame_terminatorpos + strlen("");
         $frame_rating = ord(substr($parsedFrame['data'], $frame_offset++, 1));
         $parsedFrame['counter'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset));
         $parsedFrame['email'] = $frame_emailaddress;
         $parsedFrame['rating'] = $frame_rating;
         unset($parsedFrame['data']);
     } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'RBUF' || $id3v2_majorversion == 2 && $parsedFrame['frame_name'] == 'BUF') {
         // 4.19  BUF  Recommended buffer size
         //   There may only be one 'RBUF' frame in each tag
         // <Header for 'Recommended buffer size', ID: 'RBUF'>
         // Buffer size               $xx xx xx
         // Embedded info flag        %0000000x
         // Offset to next tag        $xx xx xx xx
         $frame_offset = 0;
         $parsedFrame['buffersize'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 3));
         $frame_offset += 3;
         $frame_embeddedinfoflags = getid3_lib::BigEndian2Bin(substr($parsedFrame['data'], $frame_offset++, 1));
         $parsedFrame['flags']['embededinfo'] = (bool) substr($frame_embeddedinfoflags, 7, 1);
         $parsedFrame['nexttagoffset'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4));
         unset($parsedFrame['data']);
     } elseif ($id3v2_majorversion == 2 && $parsedFrame['frame_name'] == 'CRM') {
         // 4.20  Encrypted meta frame (ID3v2.2 only)
         //   There may be more than one 'CRM' frame in a tag,
         //   but only one with the same 'owner identifier'
         // <Header for 'Encrypted meta frame', ID: 'CRM'>
         // Owner identifier      <textstring> $00 (00)
         // Content/explanation   <textstring> $00 (00)
         // Encrypted datablock   <binary data>
         $frame_offset = 0;
         $frame_terminatorpos = strpos($parsedFrame['data'], "", $frame_offset);
         $frame_ownerid = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
         $frame_offset = $frame_terminatorpos + strlen("");
         $frame_terminatorpos = strpos($parsedFrame['data'], "", $frame_offset);
         $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
         if (ord($frame_description) === 0) {
             $frame_description = '';
         }
         $frame_offset = $frame_terminatorpos + strlen("");
         $parsedFrame['ownerid'] = $frame_ownerid;
         $parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset);
         $parsedFrame['description'] = $frame_description;
         unset($parsedFrame['data']);
     } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'AENC' || $id3v2_majorversion == 2 && $parsedFrame['frame_name'] == 'CRA') {
         // 4.21  CRA  Audio encryption
         //   There may be more than one 'AENC' frames in a tag,
         //   but only one with the same 'Owner identifier'
         // <Header for 'Audio encryption', ID: 'AENC'>
         // Owner identifier   <text string> $00
         // Preview start      $xx xx
         // Preview length     $xx xx
         // Encryption info    <binary data>
         $frame_offset = 0;
         $frame_terminatorpos = strpos($parsedFrame['data'], "", $frame_offset);
         $frame_ownerid = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
         if (ord($frame_ownerid) === 0) {
             $frame_ownerid == '';
         }
         $frame_offset = $frame_terminatorpos + strlen("");
         $parsedFrame['ownerid'] = $frame_ownerid;
         $parsedFrame['previewstart'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 2));
         $frame_offset += 2;
         $parsedFrame['previewlength'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 2));
         $frame_offset += 2;
         $parsedFrame['encryptioninfo'] = (string) substr($parsedFrame['data'], $frame_offset);
         unset($parsedFrame['data']);
     } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'LINK' || $id3v2_majorversion == 2 && $parsedFrame['frame_name'] == 'LNK') {
         // 4.22  LNK  Linked information
         //   There may be more than one 'LINK' frame in a tag,
         //   but only one with the same contents
         // <Header for 'Linked information', ID: 'LINK'>
         // ID3v2.3+ => Frame identifier   $xx xx xx xx
         // ID3v2.2  => Frame identifier   $xx xx xx
         // URL                            <text string> $00
         // ID and additional data         <text string(s)>
         $frame_offset = 0;
         if ($id3v2_majorversion == 2) {
             $parsedFrame['frameid'] = substr($parsedFrame['data'], $frame_offset, 3);
             $frame_offset += 3;
         } else {
             $parsedFrame['frameid'] = substr($parsedFrame['data'], $frame_offset, 4);
             $frame_offset += 4;
         }
         $frame_terminatorpos = strpos($parsedFrame['data'], "", $frame_offset);
         $frame_url = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
         if (ord($frame_url) === 0) {
             $frame_url = '';
         }
         $frame_offset = $frame_terminatorpos + strlen("");
         $parsedFrame['url'] = $frame_url;
         $parsedFrame['additionaldata'] = (string) substr($parsedFrame['data'], $frame_offset);
         if (!empty($parsedFrame['framenameshort']) && $parsedFrame['url']) {
             $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = getid3_lib::iconv_fallback_iso88591_utf8($parsedFrame['url']);
         }
         unset($parsedFrame['data']);
     } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'POSS') {
         // 4.21  POSS Position synchronisation frame (ID3v2.3+ only)
         //   There may only be one 'POSS' frame in each tag
         // <Head for 'Position synchronisation', ID: 'POSS'>
         // Time stamp format         $xx
         // Position                  $xx (xx ...)
         $frame_offset = 0;
         $parsedFrame['timestampformat'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
         $parsedFrame['position'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset));
         unset($parsedFrame['data']);
     } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'USER') {
         // 4.22  USER Terms of use (ID3v2.3+ only)
         //   There may be more than one 'Terms of use' frame in a tag,
         //   but only one with the same 'Language'
         // <Header for 'Terms of use frame', ID: 'USER'>
         // Text encoding        $xx
         // Language             $xx xx xx
         // The actual text      <text string according to encoding>
         $frame_offset = 0;
         $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1));
         if ($id3v2_majorversion <= 3 && $frame_textencoding > 1 || $id3v2_majorversion == 4 && $frame_textencoding > 3) {
             $info['warning'][] = 'Invalid text encoding byte (' . $frame_textencoding . ') in frame "' . $parsedFrame['frame_name'] . '" - defaulting to ISO-8859-1 encoding';
         }
         $frame_language = substr($parsedFrame['data'], $frame_offset, 3);
         $frame_offset += 3;
         $parsedFrame['language'] = $frame_language;
         $parsedFrame['languagename'] = $this->LanguageLookup($frame_language, false);
         $parsedFrame['encodingid'] = $frame_textencoding;
         $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding);
         $parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset);
         if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) {
             $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $parsedFrame['data']);
         }
         unset($parsedFrame['data']);
     } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'OWNE') {
         // 4.23  OWNE Ownership frame (ID3v2.3+ only)
         //   There may only be one 'OWNE' frame in a tag
         // <Header for 'Ownership frame', ID: 'OWNE'>
         // Text encoding     $xx
         // Price paid        <text string> $00
         // Date of purch.    <text string>
         // Seller            <text string according to encoding>
         $frame_offset = 0;
         $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1));
         if ($id3v2_majorversion <= 3 && $frame_textencoding > 1 || $id3v2_majorversion == 4 && $frame_textencoding > 3) {
             $info['warning'][] = 'Invalid text encoding byte (' . $frame_textencoding . ') in frame "' . $parsedFrame['frame_name'] . '" - defaulting to ISO-8859-1 encoding';
         }
         $parsedFrame['encodingid'] = $frame_textencoding;
         $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding);
         $frame_terminatorpos = strpos($parsedFrame['data'], "", $frame_offset);
         $frame_pricepaid = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
         $frame_offset = $frame_terminatorpos + strlen("");
         $parsedFrame['pricepaid']['currencyid'] = substr($frame_pricepaid, 0, 3);
         $parsedFrame['pricepaid']['currency'] = $this->LookupCurrencyUnits($parsedFrame['pricepaid']['currencyid']);
         $parsedFrame['pricepaid']['value'] = substr($frame_pricepaid, 3);
         $parsedFrame['purchasedate'] = substr($parsedFrame['data'], $frame_offset, 8);
         if (!$this->IsValidDateStampString($parsedFrame['purchasedate'])) {
             $parsedFrame['purchasedateunix'] = mktime(0, 0, 0, substr($parsedFrame['purchasedate'], 4, 2), substr($parsedFrame['purchasedate'], 6, 2), substr($parsedFrame['purchasedate'], 0, 4));
         }
         $frame_offset += 8;
         $parsedFrame['seller'] = (string) substr($parsedFrame['data'], $frame_offset);
         unset($parsedFrame['data']);
     } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'COMR') {
         // 4.24  COMR Commercial frame (ID3v2.3+ only)
         //   There may be more than one 'commercial frame' in a tag,
         //   but no two may be identical
         // <Header for 'Commercial frame', ID: 'COMR'>
         // Text encoding      $xx
         // Price string       <text string> $00
         // Valid until        <text string>
         // Contact URL        <text string> $00
         // Received as        $xx
         // Name of seller     <text string according to encoding> $00 (00)
         // Description        <text string according to encoding> $00 (00)
         // Picture MIME type  <string> $00
         // Seller logo        <binary data>
         $frame_offset = 0;
         $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1));
         $frame_textencoding_terminator = $this->TextEncodingTerminatorLookup($frame_textencoding);
         if ($id3v2_majorversion <= 3 && $frame_textencoding > 1 || $id3v2_majorversion == 4 && $frame_textencoding > 3) {
             $info['warning'][] = 'Invalid text encoding byte (' . $frame_textencoding . ') in frame "' . $parsedFrame['frame_name'] . '" - defaulting to ISO-8859-1 encoding';
             $frame_textencoding_terminator = "";
         }
         $frame_terminatorpos = strpos($parsedFrame['data'], "", $frame_offset);
         $frame_pricestring = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
         $frame_offset = $frame_terminatorpos + strlen("");
         $frame_rawpricearray = explode('/', $frame_pricestring);
         foreach ($frame_rawpricearray as $key => $val) {
             $frame_currencyid = substr($val, 0, 3);
             $parsedFrame['price'][$frame_currencyid]['currency'] = $this->LookupCurrencyUnits($frame_currencyid);
             $parsedFrame['price'][$frame_currencyid]['value'] = substr($val, 3);
         }
         $frame_datestring = substr($parsedFrame['data'], $frame_offset, 8);
         $frame_offset += 8;
         $frame_terminatorpos = strpos($parsedFrame['data'], "", $frame_offset);
         $frame_contacturl = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
         $frame_offset = $frame_terminatorpos + strlen("");
         $frame_receivedasid = ord(substr($parsedFrame['data'], $frame_offset++, 1));
         $frame_terminatorpos = strpos($parsedFrame['data'], $frame_textencoding_terminator, $frame_offset);
         if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator), 1)) === 0) {
             $frame_terminatorpos++;
             // strpos() fooled because 2nd byte of Unicode chars are often 0x00
         }
         $frame_sellername = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
         if (ord($frame_sellername) === 0) {
             $frame_sellername = '';
         }
         $frame_offset = $frame_terminatorpos + strlen($frame_textencoding_terminator);
         $frame_terminatorpos = strpos($parsedFrame['data'], $frame_textencoding_terminator, $frame_offset);
         if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator), 1)) === 0) {
             $frame_terminatorpos++;
             // strpos() fooled because 2nd byte of Unicode chars are often 0x00
         }
         $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
         if (ord($frame_description) === 0) {
             $frame_description = '';
         }
         $frame_offset = $frame_terminatorpos + strlen($frame_textencoding_terminator);
         $frame_terminatorpos = strpos($parsedFrame['data'], "", $frame_offset);
         $frame_mimetype = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
         $frame_offset = $frame_terminatorpos + strlen("");
         $frame_sellerlogo = substr($parsedFrame['data'], $frame_offset);
         $parsedFrame['encodingid'] = $frame_textencoding;
         $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding);
         $parsedFrame['pricevaliduntil'] = $frame_datestring;
         $parsedFrame['contacturl'] = $frame_contacturl;
         $parsedFrame['receivedasid'] = $frame_receivedasid;
         $parsedFrame['receivedas'] = $this->COMRReceivedAsLookup($frame_receivedasid);
         $parsedFrame['sellername'] = $frame_sellername;
         $parsedFrame['description'] = $frame_description;
         $parsedFrame['mime'] = $frame_mimetype;
         $parsedFrame['logo'] = $frame_sellerlogo;
         unset($parsedFrame['data']);
     } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'ENCR') {
         // 4.25  ENCR Encryption method registration (ID3v2.3+ only)
         //   There may be several 'ENCR' frames in a tag,
         //   but only one containing the same symbol
         //   and only one containing the same owner identifier
         // <Header for 'Encryption method registration', ID: 'ENCR'>
         // Owner identifier    <text string> $00
         // Method symbol       $xx
         // Encryption data     <binary data>
         $frame_offset = 0;
         $frame_terminatorpos = strpos($parsedFrame['data'], "", $frame_offset);
         $frame_ownerid = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
         if (ord($frame_ownerid) === 0) {
             $frame_ownerid = '';
         }
         $frame_offset = $frame_terminatorpos + strlen("");
         $parsedFrame['ownerid'] = $frame_ownerid;
         $parsedFrame['methodsymbol'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
         $parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset);
     } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'GRID') {
         // 4.26  GRID Group identification registration (ID3v2.3+ only)
         //   There may be several 'GRID' frames in a tag,
         //   but only one containing the same symbol
         //   and only one containing the same owner identifier
         // <Header for 'Group ID registration', ID: 'GRID'>
         // Owner identifier      <text string> $00
         // Group symbol          $xx
         // Group dependent data  <binary data>
         $frame_offset = 0;
         $frame_terminatorpos = strpos($parsedFrame['data'], "", $frame_offset);
         $frame_ownerid = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
         if (ord($frame_ownerid) === 0) {
             $frame_ownerid = '';
         }
         $frame_offset = $frame_terminatorpos + strlen("");
         $parsedFrame['ownerid'] = $frame_ownerid;
         $parsedFrame['groupsymbol'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
         $parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset);
     } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'PRIV') {
         // 4.27  PRIV Private frame (ID3v2.3+ only)
         //   The tag may contain more than one 'PRIV' frame
         //   but only with different contents
         // <Header for 'Private frame', ID: 'PRIV'>
         // Owner identifier      <text string> $00
         // The private data      <binary data>
         $frame_offset = 0;
         $frame_terminatorpos = strpos($parsedFrame['data'], "", $frame_offset);
         $frame_ownerid = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
         if (ord($frame_ownerid) === 0) {
             $frame_ownerid = '';
         }
         $frame_offset = $frame_terminatorpos + strlen("");
         $parsedFrame['ownerid'] = $frame_ownerid;
         $parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset);
     } elseif ($id3v2_majorversion >= 4 && $parsedFrame['frame_name'] == 'SIGN') {
         // 4.28  SIGN Signature frame (ID3v2.4+ only)
         //   There may be more than one 'signature frame' in a tag,
         //   but no two may be identical
         // <Header for 'Signature frame', ID: 'SIGN'>
         // Group symbol      $xx
         // Signature         <binary data>
         $frame_offset = 0;
         $parsedFrame['groupsymbol'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
         $parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset);
     } elseif ($id3v2_majorversion >= 4 && $parsedFrame['frame_name'] == 'SEEK') {
         // 4.29  SEEK Seek frame (ID3v2.4+ only)
         //   There may only be one 'seek frame' in a tag
         // <Header for 'Seek frame', ID: 'SEEK'>
         // Minimum offset to next tag       $xx xx xx xx
         $frame_offset = 0;
         $parsedFrame['data'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4));
     } elseif ($id3v2_majorversion >= 4 && $parsedFrame['frame_name'] == 'ASPI') {
         // 4.30  ASPI Audio seek point index (ID3v2.4+ only)
         //   There may only be one 'audio seek point index' frame in a tag
         // <Header for 'Seek Point Index', ID: 'ASPI'>
         // Indexed data start (S)         $xx xx xx xx
         // Indexed data length (L)        $xx xx xx xx
         // Number of index points (N)     $xx xx
         // Bits per index point (b)       $xx
         //   Then for every index point the following data is included:
         // Fraction at index (Fi)          $xx (xx)
         $frame_offset = 0;
         $parsedFrame['datastart'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4));
         $frame_offset += 4;
         $parsedFrame['indexeddatalength'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4));
         $frame_offset += 4;
         $parsedFrame['indexpoints'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 2));
         $frame_offset += 2;
         $parsedFrame['bitsperpoint'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
         $frame_bytesperpoint = ceil($parsedFrame['bitsperpoint'] / 8);
         for ($i = 0; $i < $parsedFrame['indexpoints']; $i++) {
             $parsedFrame['indexes'][$i] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesperpoint));
             $frame_offset += $frame_bytesperpoint;
         }
         unset($parsedFrame['data']);
     } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'RGAD') {
         // Replay Gain Adjustment
         // http://privatewww.essex.ac.uk/~djmrob/replaygain/file_format_id3v2.html
         //   There may only be one 'RGAD' frame in a tag
         // <Header for 'Replay Gain Adjustment', ID: 'RGAD'>
         // Peak Amplitude                      $xx $xx $xx $xx
         // Radio Replay Gain Adjustment        %aaabbbcd %dddddddd
         // Audiophile Replay Gain Adjustment   %aaabbbcd %dddddddd
         //   a - name code
         //   b - originator code
         //   c - sign bit
         //   d - replay gain adjustment
         $frame_offset = 0;
         $parsedFrame['peakamplitude'] = getid3_lib::BigEndian2Float(substr($parsedFrame['data'], $frame_offset, 4));
         $frame_offset += 4;
         $rg_track_adjustment = getid3_lib::Dec2Bin(substr($parsedFrame['data'], $frame_offset, 2));
         $frame_offset += 2;
         $rg_album_adjustment = getid3_lib::Dec2Bin(substr($parsedFrame['data'], $frame_offset, 2));
         $frame_offset += 2;
         $parsedFrame['raw']['track']['name'] = getid3_lib::Bin2Dec(substr($rg_track_adjustment, 0, 3));
         $parsedFrame['raw']['track']['originator'] = getid3_lib::Bin2Dec(substr($rg_track_adjustment, 3, 3));
         $parsedFrame['raw']['track']['signbit'] = getid3_lib::Bin2Dec(substr($rg_track_adjustment, 6, 1));
         $parsedFrame['raw']['track']['adjustment'] = getid3_lib::Bin2Dec(substr($rg_track_adjustment, 7, 9));
         $parsedFrame['raw']['album']['name'] = getid3_lib::Bin2Dec(substr($rg_album_adjustment, 0, 3));
         $parsedFrame['raw']['album']['originator'] = getid3_lib::Bin2Dec(substr($rg_album_adjustment, 3, 3));
         $parsedFrame['raw']['album']['signbit'] = getid3_lib::Bin2Dec(substr($rg_album_adjustment, 6, 1));
         $parsedFrame['raw']['album']['adjustment'] = getid3_lib::Bin2Dec(substr($rg_album_adjustment, 7, 9));
         $parsedFrame['track']['name'] = getid3_lib::RGADnameLookup($parsedFrame['raw']['track']['name']);
         $parsedFrame['track']['originator'] = getid3_lib::RGADoriginatorLookup($parsedFrame['raw']['track']['originator']);
         $parsedFrame['track']['adjustment'] = getid3_lib::RGADadjustmentLookup($parsedFrame['raw']['track']['adjustment'], $parsedFrame['raw']['track']['signbit']);
         $parsedFrame['album']['name'] = getid3_lib::RGADnameLookup($parsedFrame['raw']['album']['name']);
         $parsedFrame['album']['originator'] = getid3_lib::RGADoriginatorLookup($parsedFrame['raw']['album']['originator']);
         $parsedFrame['album']['adjustment'] = getid3_lib::RGADadjustmentLookup($parsedFrame['raw']['album']['adjustment'], $parsedFrame['raw']['album']['signbit']);
         $info['replay_gain']['track']['peak'] = $parsedFrame['peakamplitude'];
         $info['replay_gain']['track']['originator'] = $parsedFrame['track']['originator'];
         $info['replay_gain']['track']['adjustment'] = $parsedFrame['track']['adjustment'];
         $info['replay_gain']['album']['originator'] = $parsedFrame['album']['originator'];
         $info['replay_gain']['album']['adjustment'] = $parsedFrame['album']['adjustment'];
         unset($parsedFrame['data']);
     } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'CHAP') {
         // CHAP Chapters frame (ID3v2.3+ only)
         // http://id3.org/id3v2-chapters-1.0
         // <ID3v2.3 or ID3v2.4 frame header, ID: "CHAP">           (10 bytes)
         // Element ID      <text string> $00
         // Start time      $xx xx xx xx
         // End time        $xx xx xx xx
         // Start offset    $xx xx xx xx
         // End offset      $xx xx xx xx
         // <Optional embedded sub-frames>
         $frame_offset = 0;
         @(list($parsedFrame['element_id']) = explode("", $parsedFrame['data'], 2));
         $frame_offset += strlen($parsedFrame['element_id'] . "");
         $parsedFrame['time_begin'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4));
         $frame_offset += 4;
         $parsedFrame['time_end'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4));
         $frame_offset += 4;
         if (substr($parsedFrame['data'], $frame_offset, 4) != "ÿÿÿÿ") {
             // "If these bytes are all set to 0xFF then the value should be ignored and the start time value should be utilized."
             $parsedFrame['offset_begin'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4));
         }
         $frame_offset += 4;
         if (substr($parsedFrame['data'], $frame_offset, 4) != "ÿÿÿÿ") {
             // "If these bytes are all set to 0xFF then the value should be ignored and the start time value should be utilized."
             $parsedFrame['offset_end'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4));
         }
         $frame_offset += 4;
         if ($frame_offset < strlen($parsedFrame['data'])) {
             $parsedFrame['subframes'] = array();
             while ($frame_offset < strlen($parsedFrame['data'])) {
                 // <Optional embedded sub-frames>
                 $subframe = array();
                 $subframe['name'] = substr($parsedFrame['data'], $frame_offset, 4);
                 $frame_offset += 4;
                 $subframe['size'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4));
                 $frame_offset += 4;
                 $subframe['flags_raw'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 2));
                 $frame_offset += 2;
                 if ($subframe['size'] > strlen($parsedFrame['data']) - $frame_offset) {
                     $info['warning'][] = 'CHAP subframe "' . $subframe['name'] . '" at frame offset ' . $frame_offset . ' claims to be "' . $subframe['size'] . '" bytes, which is more than the available data (' . (strlen($parsedFrame['data']) - $frame_offset) . ' bytes)';
                     break;
                 }
                 $subframe_rawdata = substr($parsedFrame['data'], $frame_offset, $subframe['size']);
                 $frame_offset += $subframe['size'];
                 $subframe['encodingid'] = ord(substr($subframe_rawdata, 0, 1));
                 $subframe['text'] = substr($subframe_rawdata, 1);
                 $subframe['encoding'] = $this->TextEncodingNameLookup($subframe['encodingid']);
                 $encoding_converted_text = trim(getid3_lib::iconv_fallback($subframe['encoding'], $info['encoding'], $subframe['text']));
                 switch (substr($encoding_converted_text, 0, 2)) {
                     case "ÿþ":
                     case "þÿ":
                         switch (strtoupper($info['id3v2']['encoding'])) {
                             case 'ISO-8859-1':
                             case 'UTF-8':
                                 $encoding_converted_text = substr($encoding_converted_text, 2);
                                 // remove unwanted byte-order-marks
                                 break;
                             default:
                                 // ignore
                                 break;
                         }
                         break;
                     default:
                         // do not remove BOM
                         break;
                 }
                 if ($subframe['name'] == 'TIT2' || $subframe['name'] == 'TIT3') {
                     if ($subframe['name'] == 'TIT2') {
                         $parsedFrame['chapter_name'] = $encoding_converted_text;
                     } elseif ($subframe['name'] == 'TIT3') {
                         $parsedFrame['chapter_description'] = $encoding_converted_text;
                     }
                     $parsedFrame['subframes'][] = $subframe;
                 } else {
                     $info['warning'][] = 'ID3v2.CHAP subframe "' . $subframe['name'] . '" not handled (only TIT2 and TIT3)';
                 }
             }
             unset($subframe_rawdata, $subframe, $encoding_converted_text);
         }
         $id3v2_chapter_entry = array();
         foreach (array('id', 'time_begin', 'time_end', 'offset_begin', 'offset_end', 'chapter_name', 'chapter_description') as $id3v2_chapter_key) {
             if (isset($parsedFrame[$id3v2_chapter_key])) {
                 $id3v2_chapter_entry[$id3v2_chapter_key] = $parsedFrame[$id3v2_chapter_key];
             }
         }
         if (!isset($info['id3v2']['chapters'])) {
             $info['id3v2']['chapters'] = array();
         }
         $info['id3v2']['chapters'][] = $id3v2_chapter_entry;
         unset($id3v2_chapter_entry, $id3v2_chapter_key);
     } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'CTOC') {
         // CTOC Chapters Table Of Contents frame (ID3v2.3+ only)
         // http://id3.org/id3v2-chapters-1.0
         // <ID3v2.3 or ID3v2.4 frame header, ID: "CTOC">           (10 bytes)
         // Element ID      <text string> $00
         // CTOC flags        %xx
         // Entry count       $xx
         // Child Element ID  <string>$00   /* zero or more child CHAP or CTOC entries */
         // <Optional embedded sub-frames>
         $frame_offset = 0;
         @(list($parsedFrame['element_id']) = explode("", $parsedFrame['data'], 2));
         $frame_offset += strlen($parsedFrame['element_id'] . "");
         $ctoc_flags_raw = ord(substr($parsedFrame['data'], $frame_offset, 1));
         $frame_offset += 1;
         $parsedFrame['entry_count'] = ord(substr($parsedFrame['data'], $frame_offset, 1));
         $frame_offset += 1;
         $terminator_position = null;
         for ($i = 0; $i < $parsedFrame['entry_count']; $i++) {
             $terminator_position = strpos($parsedFrame['data'], "", $frame_offset);
             $parsedFrame['child_element_ids'][$i] = substr($parsedFrame['data'], $frame_offset, $terminator_position - $frame_offset);
             $frame_offset = $terminator_position + 1;
         }
         $parsedFrame['ctoc_flags']['ordered'] = (bool) ($ctoc_flags_raw & 0x1);
         $parsedFrame['ctoc_flags']['top_level'] = (bool) ($ctoc_flags_raw & 0x3);
         unset($ctoc_flags_raw, $terminator_position);
         if ($frame_offset < strlen($parsedFrame['data'])) {
             $parsedFrame['subframes'] = array();
             while ($frame_offset < strlen($parsedFrame['data'])) {
                 // <Optional embedded sub-frames>
                 $subframe = array();
                 $subframe['name'] = substr($parsedFrame['data'], $frame_offset, 4);
                 $frame_offset += 4;
                 $subframe['size'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4));
                 $frame_offset += 4;
                 $subframe['flags_raw'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 2));
                 $frame_offset += 2;
                 if ($subframe['size'] > strlen($parsedFrame['data']) - $frame_offset) {
                     $info['warning'][] = 'CTOS subframe "' . $subframe['name'] . '" at frame offset ' . $frame_offset . ' claims to be "' . $subframe['size'] . '" bytes, which is more than the available data (' . (strlen($parsedFrame['data']) - $frame_offset) . ' bytes)';
                     break;
                 }
                 $subframe_rawdata = substr($parsedFrame['data'], $frame_offset, $subframe['size']);
                 $frame_offset += $subframe['size'];
                 $subframe['encodingid'] = ord(substr($subframe_rawdata, 0, 1));
                 $subframe['text'] = substr($subframe_rawdata, 1);
                 $subframe['encoding'] = $this->TextEncodingNameLookup($subframe['encodingid']);
                 $encoding_converted_text = trim(getid3_lib::iconv_fallback($subframe['encoding'], $info['encoding'], $subframe['text']));
                 switch (substr($encoding_converted_text, 0, 2)) {
                     case "ÿþ":
                     case "þÿ":
                         switch (strtoupper($info['id3v2']['encoding'])) {
                             case 'ISO-8859-1':
                             case 'UTF-8':
                                 $encoding_converted_text = substr($encoding_converted_text, 2);
                                 // remove unwanted byte-order-marks
                                 break;
                             default:
                                 // ignore
                                 break;
                         }
                         break;
                     default:
                         // do not remove BOM
                         break;
                 }
                 if ($subframe['name'] == 'TIT2' || $subframe['name'] == 'TIT3') {
                     if ($subframe['name'] == 'TIT2') {
                         $parsedFrame['toc_name'] = $encoding_converted_text;
                     } elseif ($subframe['name'] == 'TIT3') {
                         $parsedFrame['toc_description'] = $encoding_converted_text;
                     }
                     $parsedFrame['subframes'][] = $subframe;
                 } else {
                     $info['warning'][] = 'ID3v2.CTOC subframe "' . $subframe['name'] . '" not handled (only TIT2 and TIT3)';
                 }
             }
             unset($subframe_rawdata, $subframe, $encoding_converted_text);
         }
     }
     return true;
 }
 function FLACparseSTREAMINFO($METAdataBlockData, &$ThisFileInfo)
 {
     $offset = 0;
     $ThisFileInfo['flac']['STREAMINFO']['min_block_size'] = getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 2));
     $offset += 2;
     $ThisFileInfo['flac']['STREAMINFO']['max_block_size'] = getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 2));
     $offset += 2;
     $ThisFileInfo['flac']['STREAMINFO']['min_frame_size'] = getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 3));
     $offset += 3;
     $ThisFileInfo['flac']['STREAMINFO']['max_frame_size'] = getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 3));
     $offset += 3;
     $SampleRateChannelsSampleBitsStreamSamples = getid3_lib::BigEndian2Bin(substr($METAdataBlockData, $offset, 8));
     $ThisFileInfo['flac']['STREAMINFO']['sample_rate'] = getid3_lib::Bin2Dec(substr($SampleRateChannelsSampleBitsStreamSamples, 0, 20));
     $ThisFileInfo['flac']['STREAMINFO']['channels'] = getid3_lib::Bin2Dec(substr($SampleRateChannelsSampleBitsStreamSamples, 20, 3)) + 1;
     $ThisFileInfo['flac']['STREAMINFO']['bits_per_sample'] = getid3_lib::Bin2Dec(substr($SampleRateChannelsSampleBitsStreamSamples, 23, 5)) + 1;
     $ThisFileInfo['flac']['STREAMINFO']['samples_stream'] = getid3_lib::Bin2Dec(substr($SampleRateChannelsSampleBitsStreamSamples, 28, 36));
     $offset += 8;
     $ThisFileInfo['flac']['STREAMINFO']['audio_signature'] = substr($METAdataBlockData, $offset, 16);
     $offset += 16;
     if (!empty($ThisFileInfo['flac']['STREAMINFO']['sample_rate'])) {
         $ThisFileInfo['audio']['bitrate_mode'] = 'vbr';
         $ThisFileInfo['audio']['sample_rate'] = $ThisFileInfo['flac']['STREAMINFO']['sample_rate'];
         $ThisFileInfo['audio']['channels'] = $ThisFileInfo['flac']['STREAMINFO']['channels'];
         $ThisFileInfo['audio']['bits_per_sample'] = $ThisFileInfo['flac']['STREAMINFO']['bits_per_sample'];
         $ThisFileInfo['playtime_seconds'] = $ThisFileInfo['flac']['STREAMINFO']['samples_stream'] / $ThisFileInfo['flac']['STREAMINFO']['sample_rate'];
         $ThisFileInfo['audio']['bitrate'] = ($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8 / $ThisFileInfo['playtime_seconds'];
     } else {
         $ThisFileInfo['error'][] = 'Corrupt METAdata block: STREAMINFO';
         return false;
     }
     return true;
 }
 public function Analyze()
 {
     $getid3 = $this->getid3;
     $getid3->info['mpeg']['video']['raw'] = array();
     $info_mpeg_video =& $getid3->info['mpeg']['video'];
     $info_mpeg_video_raw =& $info_mpeg_video['raw'];
     $getid3->info['video'] = array();
     $info_video =& $getid3->info['video'];
     $getid3->include_module('audio.mp3');
     if ($getid3->info['avdataend'] <= $getid3->info['avdataoffset']) {
         throw new getid3_exception('"avdataend" (' . $getid3->info['avdataend'] . ') is unexpectedly less-than-or-equal-to "avdataoffset" (' . $getid3->info['avdataoffset'] . ')');
     }
     $getid3->info['fileformat'] = 'mpeg';
     fseek($getid3->fp, $getid3->info['avdataoffset'], SEEK_SET);
     $mpeg_stream_data = fread($getid3->fp, min(100000, $getid3->info['avdataend'] - $getid3->info['avdataoffset']));
     $mpeg_stream_data_length = strlen($mpeg_stream_data);
     $video_chunk_offset = 0;
     while (substr($mpeg_stream_data, $video_chunk_offset++, 4) !== getid3_mpeg::VIDEO_SEQUENCE_HEADER) {
         if ($video_chunk_offset >= $mpeg_stream_data_length) {
             throw new getid3_exception('Could not find start of video block in the first 100,000 bytes (or before end of file) - this might not be an MPEG-video file?');
         }
     }
     // Start code                       32 bits
     // horizontal frame size            12 bits
     // vertical frame size              12 bits
     // pixel aspect ratio                4 bits
     // frame rate                        4 bits
     // bitrate                          18 bits
     // marker bit                        1 bit
     // VBV buffer size                  10 bits
     // constrained parameter flag        1 bit
     // intra quant. matrix flag          1 bit
     // intra quant. matrix values      512 bits (present if matrix flag == 1)
     // non-intra quant. matrix flag      1 bit
     // non-intra quant. matrix values  512 bits (present if matrix flag == 1)
     $info_video['dataformat'] = 'mpeg';
     $video_chunk_offset += strlen(getid3_mpeg::VIDEO_SEQUENCE_HEADER) - 1;
     $frame_size_dword = getid3_lib::BigEndian2Int(substr($mpeg_stream_data, $video_chunk_offset, 3));
     $video_chunk_offset += 3;
     $aspect_ratio_frame_rate_dword = getid3_lib::BigEndian2Int(substr($mpeg_stream_data, $video_chunk_offset, 1));
     $video_chunk_offset += 1;
     $assorted_information = getid3_lib::BigEndian2Bin(substr($mpeg_stream_data, $video_chunk_offset, 4));
     $video_chunk_offset += 4;
     $info_mpeg_video_raw['framesize_horizontal'] = ($frame_size_dword & 0xfff000) >> 12;
     // 12 bits for horizontal frame size
     $info_mpeg_video_raw['framesize_vertical'] = $frame_size_dword & 0xfff;
     // 12 bits for vertical frame size
     $info_mpeg_video_raw['pixel_aspect_ratio'] = ($aspect_ratio_frame_rate_dword & 0xf0) >> 4;
     $info_mpeg_video_raw['frame_rate'] = $aspect_ratio_frame_rate_dword & 0xf;
     $info_mpeg_video['framesize_horizontal'] = $info_mpeg_video_raw['framesize_horizontal'];
     $info_mpeg_video['framesize_vertical'] = $info_mpeg_video_raw['framesize_vertical'];
     $info_mpeg_video['pixel_aspect_ratio'] = $this->MPEGvideoAspectRatioLookup($info_mpeg_video_raw['pixel_aspect_ratio']);
     $info_mpeg_video['pixel_aspect_ratio_text'] = $this->MPEGvideoAspectRatioTextLookup($info_mpeg_video_raw['pixel_aspect_ratio']);
     $info_mpeg_video['frame_rate'] = $this->MPEGvideoFramerateLookup($info_mpeg_video_raw['frame_rate']);
     $info_mpeg_video_raw['bitrate'] = bindec(substr($assorted_information, 0, 18));
     $info_mpeg_video_raw['marker_bit'] = (bool) bindec($assorted_information[18]);
     $info_mpeg_video_raw['vbv_buffer_size'] = bindec(substr($assorted_information, 19, 10));
     $info_mpeg_video_raw['constrained_param_flag'] = (bool) bindec($assorted_information[29]);
     $info_mpeg_video_raw['intra_quant_flag'] = (bool) bindec($assorted_information[30]);
     if ($info_mpeg_video_raw['intra_quant_flag']) {
         // read 512 bits
         $info_mpeg_video_raw['intra_quant'] = getid3_lib::BigEndian2Bin(substr($mpeg_stream_data, $video_chunk_offset, 64));
         $video_chunk_offset += 64;
         $info_mpeg_video_raw['non_intra_quant_flag'] = (bool) bindec($info_mpeg_video_raw['intra_quant'][511]);
         $info_mpeg_video_raw['intra_quant'] = bindec($assorted_information[31]) . substr(getid3_lib::BigEndian2Bin(substr($mpeg_stream_data, $video_chunk_offset, 64)), 0, 511);
         if ($info_mpeg_video_raw['non_intra_quant_flag']) {
             $info_mpeg_video_raw['non_intra_quant'] = substr($mpeg_stream_data, $video_chunk_offset, 64);
             $video_chunk_offset += 64;
         }
     } else {
         $info_mpeg_video_raw['non_intra_quant_flag'] = (bool) bindec($assorted_information[31]);
         if ($info_mpeg_video_raw['non_intra_quant_flag']) {
             $info_mpeg_video_raw['non_intra_quant'] = substr($mpeg_stream_data, $video_chunk_offset, 64);
             $video_chunk_offset += 64;
         }
     }
     if ($info_mpeg_video_raw['bitrate'] == 0x3ffff) {
         // 18 set bits
         $getid3->warning('This version of getID3() cannot determine average bitrate of VBR MPEG video files');
         $info_mpeg_video['bitrate_mode'] = 'vbr';
     } else {
         $info_mpeg_video['bitrate'] = $info_mpeg_video_raw['bitrate'] * 400;
         $info_mpeg_video['bitrate_mode'] = 'cbr';
         $info_video['bitrate'] = $info_mpeg_video['bitrate'];
     }
     $info_video['resolution_x'] = $info_mpeg_video['framesize_horizontal'];
     $info_video['resolution_y'] = $info_mpeg_video['framesize_vertical'];
     $info_video['frame_rate'] = $info_mpeg_video['frame_rate'];
     $info_video['bitrate_mode'] = $info_mpeg_video['bitrate_mode'];
     $info_video['pixel_aspect_ratio'] = $info_mpeg_video['pixel_aspect_ratio'];
     $info_video['lossless'] = false;
     $info_video['bits_per_sample'] = 24;
     //0x000001B3 begins the sequence_header of every MPEG video stream.
     //But in MPEG-2, this header must immediately be followed by an
     //extension_start_code (0x000001B5) with a sequence_extension ID (1).
     //(This extension contains all the additional MPEG-2 stuff.)
     //MPEG-1 doesn't have this extension, so that's a sure way to tell the
     //difference between MPEG-1 and MPEG-2 video streams.
     $info_video['codec'] = substr($mpeg_stream_data, $video_chunk_offset, 4) == getid3_mpeg::VIDEO_EXTENSION_START ? 'MPEG-2' : 'MPEG-1';
     $audio_chunk_offset = 0;
     while (true) {
         while (substr($mpeg_stream_data, $audio_chunk_offset++, 4) !== getid3_mpeg::AUDIO_START) {
             if ($audio_chunk_offset >= $mpeg_stream_data_length) {
                 break 2;
             }
         }
         for ($i = 0; $i <= 7; $i++) {
             // some files have the MPEG-audio header 8 bytes after the end of the $00 $00 $01 $C0 signature, some have it up to 13 bytes (or more?) after
             // I have no idea why or what the difference is, so this is a stupid hack.
             // If anybody has any better idea of what's going on, please let me know - info@getid3.org
             // make copy of info
             $dummy = $getid3->info;
             // clone getid3 - better safe than sorry
             $clone = clone $this->getid3;
             // check
             $mp3 = new getid3_mp3($clone);
             if ($mp3->decodeMPEGaudioHeader($getid3->fp, $audio_chunk_offset + 3 + 8 + $i, $dummy, false)) {
                 $getid3->info = $dummy;
                 $getid3->info['audio']['bitrate_mode'] = 'cbr';
                 $getid3->info['audio']['lossless'] = false;
                 break 2;
             }
             // destroy copy
             unset($dummy);
         }
     }
     // Temporary hack to account for interleaving overhead:
     if (!empty($info_video['bitrate']) && !empty($getid3->info['audio']['bitrate'])) {
         $getid3->info['playtime_seconds'] = ($getid3->info['avdataend'] - $getid3->info['avdataoffset']) * 8 / ($info_video['bitrate'] + $getid3->info['audio']['bitrate']);
         // Interleaved MPEG audio/video files have a certain amount of overhead that varies
         // by both video and audio bitrates, and not in any sensible, linear/logarithmic patter
         // Use interpolated lookup tables to approximately guess how much is overhead, because
         // playtime is calculated as filesize / total-bitrate
         $getid3->info['playtime_seconds'] *= $this->MPEGsystemNonOverheadPercentage($info_video['bitrate'], $getid3->info['audio']['bitrate']);
         //switch ($info_video['bitrate']) {
         //    case('5000000'):
         //        $multiplier = 0.93292642112380355828048824319889;
         //        break;
         //    case('5500000'):
         //        $multiplier = 0.93582895375200989965359777343219;
         //        break;
         //    case('6000000'):
         //        $multiplier = 0.93796247714820932532911373859139;
         //        break;
         //    case('7000000'):
         //        $multiplier = 0.9413264083635103463010117778776;
         //        break;
         //    default:
         //        $multiplier = 1;
         //        break;
         //}
         //$getid3->info['playtime_seconds'] *= $multiplier;
         //$getid3->warning('Interleaved MPEG audio/video playtime may be inaccurate. With current hack should be within a few seconds of accurate. Report to info@getid3.org if off by more than 10 seconds.');
         if ($info_video['bitrate'] < 50000) {
             $getid3->warning('Interleaved MPEG audio/video playtime may be slightly inaccurate for video bitrates below 100kbps. Except in extreme low-bitrate situations, error should be less than 1%. Report to info@getid3.org if greater than this.');
         }
     }
     return true;
 }
 public function Analyze()
 {
     $info =& $this->getid3->info;
     $info['fileformat'] = 'mpeg';
     $this->fseek($info['avdataoffset']);
     $MPEGstreamData = $this->fread($this->getid3->option_fread_buffer_size);
     $MPEGstreamBaseOffset = 0;
     // how far are we from the beginning of the file data ($info['avdataoffset'])
     $MPEGstreamDataOffset = 0;
     // how far are we from the beginning of the buffer data (~32kB)
     $StartCodeValue = false;
     $prevStartCodeValue = false;
     $GOPcounter = -1;
     $FramesByGOP = array();
     $ParsedAVchannels = array();
     do {
         //echo $MPEGstreamDataOffset.' vs '.(strlen($MPEGstreamData) - 1024).'<Br>';
         if ($MPEGstreamDataOffset > strlen($MPEGstreamData) - 16384) {
             // buffer running low, get more data
             //echo 'reading more data<br>';
             $MPEGstreamData .= $this->fread($this->getid3->option_fread_buffer_size);
             if (strlen($MPEGstreamData) > $this->getid3->option_fread_buffer_size) {
                 $MPEGstreamData = substr($MPEGstreamData, $MPEGstreamDataOffset);
                 $MPEGstreamBaseOffset += $MPEGstreamDataOffset;
                 $MPEGstreamDataOffset = 0;
             }
         }
         if (($StartCodeOffset = strpos($MPEGstreamData, self::START_CODE_BASE, $MPEGstreamDataOffset)) === false) {
             //echo 'no more start codes found.<br>';
             break;
         } else {
             $MPEGstreamDataOffset = $StartCodeOffset;
             $prevStartCodeValue = $StartCodeValue;
             $StartCodeValue = ord(substr($MPEGstreamData, $StartCodeOffset + 3, 1));
             //echo 'Found "'.strtoupper(dechex($StartCodeValue)).'" at offset '.($MPEGstreamBaseOffset + $StartCodeOffset).' ($MPEGstreamDataOffset = '.$MPEGstreamDataOffset.')<br>';
         }
         $MPEGstreamDataOffset += 4;
         switch ($StartCodeValue) {
             case 0x0:
                 // picture_start_code
                 if ($info['mpeg']['video']['bitrate_mode'] == 'vbr') {
                     $bitstream = getid3_lib::BigEndian2Bin(substr($MPEGstreamData, $StartCodeOffset + 4, 4));
                     $bitstreamoffset = 0;
                     $PictureHeader = array();
                     $PictureHeader['temporal_reference'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 10);
                     // 10-bit unsigned integer associated with each input picture. It is incremented by one, modulo 1024, for each input frame. When a frame is coded as two fields the temporal reference in the picture header of both fields is the same. Following a group start header the temporal reference of the earliest picture (in display order) shall be reset to zero.
                     $PictureHeader['picture_coding_type'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 3);
                     //  3 bits for picture_coding_type
                     $PictureHeader['vbv_delay'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 16);
                     // 16 bits for vbv_delay
                     //... etc
                     $FramesByGOP[$GOPcounter][] = $PictureHeader;
                 }
                 break;
             case 0xb3:
                 // sequence_header_code
                 /*
                 Note: purposely doing the less-pretty (and probably a bit slower) method of using string of bits rather than bitwise operations.
                       Mostly because PHP 32-bit doesn't handle unsigned integers well for bitwise operation.
                       Also the MPEG stream is designed as a bitstream and often doesn't align nicely with byte boundaries.
                 */
                 $info['video']['codec'] = 'MPEG-1';
                 // will be updated if extension_start_code found
                 $bitstream = getid3_lib::BigEndian2Bin(substr($MPEGstreamData, $StartCodeOffset + 4, 8));
                 $bitstreamoffset = 0;
                 $info['mpeg']['video']['raw']['horizontal_size_value'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 12);
                 // 12 bits for horizontal frame size. Note: horizontal_size_extension, if present, will add 2 most-significant bits to this value
                 $info['mpeg']['video']['raw']['vertical_size_value'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 12);
                 // 12 bits for vertical frame size.   Note: vertical_size_extension,   if present, will add 2 most-significant bits to this value
                 $info['mpeg']['video']['raw']['aspect_ratio_information'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 4);
                 //  4 bits for aspect_ratio_information
                 $info['mpeg']['video']['raw']['frame_rate_code'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 4);
                 //  4 bits for Frame Rate id code
                 $info['mpeg']['video']['raw']['bitrate'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 18);
                 // 18 bits for bit_rate_value (18 set bits = VBR, otherwise bitrate = this value * 400)
                 $marker_bit = self::readBitsFromStream($bitstream, $bitstreamoffset, 1);
                 // The term "marker_bit" indicates a one bit field in which the value zero is forbidden. These marker bits are introduced at several points in the syntax to avoid start code emulation.
                 $info['mpeg']['video']['raw']['vbv_buffer_size'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 10);
                 // 10 bits vbv_buffer_size_value
                 $info['mpeg']['video']['raw']['constrained_param_flag'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 1);
                 //  1 bit flag: constrained_param_flag
                 $info['mpeg']['video']['raw']['load_intra_quantiser_matrix'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 1);
                 //  1 bit flag: load_intra_quantiser_matrix
                 if ($info['mpeg']['video']['raw']['load_intra_quantiser_matrix']) {
                     $bitstream .= getid3_lib::BigEndian2Bin(substr($MPEGstreamData, $StartCodeOffset + 12, 64));
                     for ($i = 0; $i < 64; $i++) {
                         $info['mpeg']['video']['raw']['intra_quantiser_matrix'][$i] = self::readBitsFromStream($bitstream, $bitstreamoffset, 8);
                     }
                 }
                 $info['mpeg']['video']['raw']['load_non_intra_quantiser_matrix'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 1);
                 if ($info['mpeg']['video']['raw']['load_non_intra_quantiser_matrix']) {
                     $bitstream .= getid3_lib::BigEndian2Bin(substr($MPEGstreamData, $StartCodeOffset + 12 + ($info['mpeg']['video']['raw']['load_intra_quantiser_matrix'] ? 64 : 0), 64));
                     for ($i = 0; $i < 64; $i++) {
                         $info['mpeg']['video']['raw']['non_intra_quantiser_matrix'][$i] = self::readBitsFromStream($bitstream, $bitstreamoffset, 8);
                     }
                 }
                 $info['mpeg']['video']['pixel_aspect_ratio'] = self::videoAspectRatioLookup($info['mpeg']['video']['raw']['aspect_ratio_information']);
                 $info['mpeg']['video']['pixel_aspect_ratio_text'] = self::videoAspectRatioTextLookup($info['mpeg']['video']['raw']['aspect_ratio_information']);
                 $info['mpeg']['video']['frame_rate'] = self::videoFramerateLookup($info['mpeg']['video']['raw']['frame_rate_code']);
                 if ($info['mpeg']['video']['raw']['bitrate'] == 0x3ffff) {
                     // 18 set bits = VBR
                     //$this->warning('This version of getID3() ['.$this->getid3->version().'] cannot determine average bitrate of VBR MPEG video files');
                     $info['mpeg']['video']['bitrate_mode'] = 'vbr';
                 } else {
                     $info['mpeg']['video']['bitrate'] = $info['mpeg']['video']['raw']['bitrate'] * 400;
                     $info['mpeg']['video']['bitrate_mode'] = 'cbr';
                     $info['video']['bitrate'] = $info['mpeg']['video']['bitrate'];
                 }
                 $info['video']['resolution_x'] = $info['mpeg']['video']['raw']['horizontal_size_value'];
                 $info['video']['resolution_y'] = $info['mpeg']['video']['raw']['vertical_size_value'];
                 $info['video']['frame_rate'] = $info['mpeg']['video']['frame_rate'];
                 $info['video']['bitrate_mode'] = $info['mpeg']['video']['bitrate_mode'];
                 $info['video']['pixel_aspect_ratio'] = $info['mpeg']['video']['pixel_aspect_ratio'];
                 $info['video']['lossless'] = false;
                 $info['video']['bits_per_sample'] = 24;
                 break;
             case 0xb5:
                 // extension_start_code
                 $info['video']['codec'] = 'MPEG-2';
                 $bitstream = getid3_lib::BigEndian2Bin(substr($MPEGstreamData, $StartCodeOffset + 4, 8));
                 // 48 bits for Sequence Extension ID; 61 bits for Sequence Display Extension ID; 59 bits for Sequence Scalable Extension ID
                 $bitstreamoffset = 0;
                 $info['mpeg']['video']['raw']['extension_start_code_identifier'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 4);
                 //  4 bits for extension_start_code_identifier
                 //echo $info['mpeg']['video']['raw']['extension_start_code_identifier'].'<br>';
                 switch ($info['mpeg']['video']['raw']['extension_start_code_identifier']) {
                     case 1:
                         // 0001 Sequence Extension ID
                         $info['mpeg']['video']['raw']['profile_and_level_indication'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 8);
                         //  8 bits for profile_and_level_indication
                         $info['mpeg']['video']['raw']['progressive_sequence'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 1);
                         //  1 bit flag: progressive_sequence
                         $info['mpeg']['video']['raw']['chroma_format'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 2);
                         //  2 bits for chroma_format
                         $info['mpeg']['video']['raw']['horizontal_size_extension'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 2);
                         //  2 bits for horizontal_size_extension
                         $info['mpeg']['video']['raw']['vertical_size_extension'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 2);
                         //  2 bits for vertical_size_extension
                         $info['mpeg']['video']['raw']['bit_rate_extension'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 12);
                         // 12 bits for bit_rate_extension
                         $marker_bit = self::readBitsFromStream($bitstream, $bitstreamoffset, 1);
                         // The term "marker_bit" indicates a one bit field in which the value zero is forbidden. These marker bits are introduced at several points in the syntax to avoid start code emulation.
                         $info['mpeg']['video']['raw']['vbv_buffer_size_extension'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 8);
                         //  8 bits for vbv_buffer_size_extension
                         $info['mpeg']['video']['raw']['low_delay'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 1);
                         //  1 bit flag: low_delay
                         $info['mpeg']['video']['raw']['frame_rate_extension_n'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 2);
                         //  2 bits for frame_rate_extension_n
                         $info['mpeg']['video']['raw']['frame_rate_extension_d'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 5);
                         //  5 bits for frame_rate_extension_d
                         $info['video']['resolution_x'] = $info['mpeg']['video']['raw']['horizontal_size_extension'] << 12 | $info['mpeg']['video']['raw']['horizontal_size_value'];
                         $info['video']['resolution_y'] = $info['mpeg']['video']['raw']['vertical_size_extension'] << 12 | $info['mpeg']['video']['raw']['vertical_size_value'];
                         $info['video']['interlaced'] = !$info['mpeg']['video']['raw']['progressive_sequence'];
                         $info['mpeg']['video']['interlaced'] = !$info['mpeg']['video']['raw']['progressive_sequence'];
                         $info['mpeg']['video']['chroma_format'] = self::chromaFormatTextLookup($info['mpeg']['video']['raw']['chroma_format']);
                         break;
                     case 2:
                         // 0010 Sequence Display Extension ID
                         $info['mpeg']['video']['raw']['video_format'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 3);
                         //  3 bits for video_format
                         $info['mpeg']['video']['raw']['colour_description'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 1);
                         //  1 bit flag: colour_description
                         if ($info['mpeg']['video']['raw']['colour_description']) {
                             $info['mpeg']['video']['raw']['colour_primaries'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 8);
                             //  8 bits for colour_primaries
                             $info['mpeg']['video']['raw']['transfer_characteristics'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 8);
                             //  8 bits for transfer_characteristics
                             $info['mpeg']['video']['raw']['matrix_coefficients'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 8);
                             //  8 bits for matrix_coefficients
                         }
                         $info['mpeg']['video']['raw']['display_horizontal_size'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 14);
                         // 14 bits for display_horizontal_size
                         $marker_bit = self::readBitsFromStream($bitstream, $bitstreamoffset, 1);
                         // The term "marker_bit" indicates a one bit field in which the value zero is forbidden. These marker bits are introduced at several points in the syntax to avoid start code emulation.
                         $info['mpeg']['video']['raw']['display_vertical_size'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 14);
                         // 14 bits for display_vertical_size
                         $info['mpeg']['video']['video_format'] = self::videoFormatTextLookup($info['mpeg']['video']['raw']['video_format']);
                         break;
                     case 3:
                         // 0011 Quant Matrix Extension ID
                         break;
                     case 5:
                         // 0101 Sequence Scalable Extension ID
                         $info['mpeg']['video']['raw']['scalable_mode'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 2);
                         //  2 bits for scalable_mode
                         $info['mpeg']['video']['raw']['layer_id'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 4);
                         //  4 bits for layer_id
                         if ($info['mpeg']['video']['raw']['scalable_mode'] == 1) {
                             // "spatial scalability"
                             $info['mpeg']['video']['raw']['lower_layer_prediction_horizontal_size'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 14);
                             // 14 bits for lower_layer_prediction_horizontal_size
                             $marker_bit = self::readBitsFromStream($bitstream, $bitstreamoffset, 1);
                             // The term "marker_bit" indicates a one bit field in which the value zero is forbidden. These marker bits are introduced at several points in the syntax to avoid start code emulation.
                             $info['mpeg']['video']['raw']['lower_layer_prediction_vertical_size'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 14);
                             // 14 bits for lower_layer_prediction_vertical_size
                             $info['mpeg']['video']['raw']['horizontal_subsampling_factor_m'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 5);
                             //  5 bits for horizontal_subsampling_factor_m
                             $info['mpeg']['video']['raw']['horizontal_subsampling_factor_n'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 5);
                             //  5 bits for horizontal_subsampling_factor_n
                             $info['mpeg']['video']['raw']['vertical_subsampling_factor_m'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 5);
                             //  5 bits for vertical_subsampling_factor_m
                             $info['mpeg']['video']['raw']['vertical_subsampling_factor_n'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 5);
                             //  5 bits for vertical_subsampling_factor_n
                         } elseif ($info['mpeg']['video']['raw']['scalable_mode'] == 3) {
                             // "temporal scalability"
                             $info['mpeg']['video']['raw']['picture_mux_enable'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 1);
                             //  1 bit flag: picture_mux_enable
                             if ($info['mpeg']['video']['raw']['picture_mux_enable']) {
                                 $info['mpeg']['video']['raw']['mux_to_progressive_sequence'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 1);
                                 //  1 bit flag: mux_to_progressive_sequence
                             }
                             $info['mpeg']['video']['raw']['picture_mux_order'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 3);
                             //  3 bits for picture_mux_order
                             $info['mpeg']['video']['raw']['picture_mux_factor'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 3);
                             //  3 bits for picture_mux_factor
                         }
                         $info['mpeg']['video']['scalable_mode'] = self::scalableModeTextLookup($info['mpeg']['video']['raw']['scalable_mode']);
                         break;
                     case 7:
                         // 0111 Picture Display Extension ID
                         break;
                     case 8:
                         // 1000 Picture Coding Extension ID
                         $info['mpeg']['video']['raw']['f_code_00'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 4);
                         // 4 bits for f_code[0][0] (forward horizontal)
                         $info['mpeg']['video']['raw']['f_code_01'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 4);
                         // 4 bits for f_code[0][1] (forward vertical)
                         $info['mpeg']['video']['raw']['f_code_10'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 4);
                         // 4 bits for f_code[1][0] (backward horizontal)
                         $info['mpeg']['video']['raw']['f_code_11'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 4);
                         // 4 bits for f_code[1][1] (backward vertical)
                         $info['mpeg']['video']['raw']['intra_dc_precision'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 2);
                         // 2 bits for intra_dc_precision
                         $info['mpeg']['video']['raw']['picture_structure'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 2);
                         // 2 bits for picture_structure
                         $info['mpeg']['video']['raw']['top_field_first'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 1);
                         // 1 bit flag: top_field_first
                         $info['mpeg']['video']['raw']['frame_pred_frame_dct'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 1);
                         // 1 bit flag: frame_pred_frame_dct
                         $info['mpeg']['video']['raw']['concealment_motion_vectors'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 1);
                         // 1 bit flag: concealment_motion_vectors
                         $info['mpeg']['video']['raw']['q_scale_type'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 1);
                         // 1 bit flag: q_scale_type
                         $info['mpeg']['video']['raw']['intra_vlc_format'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 1);
                         // 1 bit flag: intra_vlc_format
                         $info['mpeg']['video']['raw']['alternate_scan'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 1);
                         // 1 bit flag: alternate_scan
                         $info['mpeg']['video']['raw']['repeat_first_field'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 1);
                         // 1 bit flag: repeat_first_field
                         $info['mpeg']['video']['raw']['chroma_420_type'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 1);
                         // 1 bit flag: chroma_420_type
                         $info['mpeg']['video']['raw']['progressive_frame'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 1);
                         // 1 bit flag: progressive_frame
                         $info['mpeg']['video']['raw']['composite_display_flag'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 1);
                         // 1 bit flag: composite_display_flag
                         if ($info['mpeg']['video']['raw']['composite_display_flag']) {
                             $info['mpeg']['video']['raw']['v_axis'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 1);
                             // 1 bit flag: v_axis
                             $info['mpeg']['video']['raw']['field_sequence'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 3);
                             // 3 bits for field_sequence
                             $info['mpeg']['video']['raw']['sub_carrier'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 1);
                             // 1 bit flag: sub_carrier
                             $info['mpeg']['video']['raw']['burst_amplitude'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 7);
                             // 7 bits for burst_amplitude
                             $info['mpeg']['video']['raw']['sub_carrier_phase'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 8);
                             // 8 bits for sub_carrier_phase
                         }
                         $info['mpeg']['video']['intra_dc_precision_bits'] = $info['mpeg']['video']['raw']['intra_dc_precision'] + 8;
                         $info['mpeg']['video']['picture_structure'] = self::pictureStructureTextLookup($info['mpeg']['video']['raw']['picture_structure']);
                         break;
                     case 9:
                         // 1001 Picture Spatial Scalable Extension ID
                         break;
                     case 10:
                         // 1010 Picture Temporal Scalable Extension ID
                         break;
                     default:
                         $this->warning('Unexpected $info[mpeg][video][raw][extension_start_code_identifier] value of ' . $info['mpeg']['video']['raw']['extension_start_code_identifier']);
                         break;
                 }
                 break;
             case 0xb8:
                 // group_of_pictures_header
                 $GOPcounter++;
                 if ($info['mpeg']['video']['bitrate_mode'] == 'vbr') {
                     $bitstream = getid3_lib::BigEndian2Bin(substr($MPEGstreamData, $StartCodeOffset + 4, 4));
                     // 27 bits needed for group_of_pictures_header
                     $bitstreamoffset = 0;
                     $GOPheader = array();
                     $GOPheader['byte_offset'] = $MPEGstreamBaseOffset + $StartCodeOffset;
                     $GOPheader['drop_frame_flag'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 1);
                     //  1 bit flag: drop_frame_flag
                     $GOPheader['time_code_hours'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 5);
                     //  5 bits for time_code_hours
                     $GOPheader['time_code_minutes'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 6);
                     //  6 bits for time_code_minutes
                     $marker_bit = self::readBitsFromStream($bitstream, $bitstreamoffset, 1);
                     // The term "marker_bit" indicates a one bit field in which the value zero is forbidden. These marker bits are introduced at several points in the syntax to avoid start code emulation.
                     $GOPheader['time_code_seconds'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 6);
                     //  6 bits for time_code_seconds
                     $GOPheader['time_code_pictures'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 6);
                     //  6 bits for time_code_pictures
                     $GOPheader['closed_gop'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 1);
                     //  1 bit flag: closed_gop
                     $GOPheader['broken_link'] = self::readBitsFromStream($bitstream, $bitstreamoffset, 1);
                     //  1 bit flag: broken_link
                     $time_code_separator = $GOPheader['drop_frame_flag'] ? ';' : ':';
                     // While non-drop time code is displayed with colons separating the digit pairs—"HH:MM:SS:FF"—drop frame is usually represented with a semi-colon (;) or period (.) as the divider between all the digit pairs—"HH;MM;SS;FF", "HH.MM.SS.FF"
                     $GOPheader['time_code'] = sprintf('%02d' . $time_code_separator . '%02d' . $time_code_separator . '%02d' . $time_code_separator . '%02d', $GOPheader['time_code_hours'], $GOPheader['time_code_minutes'], $GOPheader['time_code_seconds'], $GOPheader['time_code_pictures']);
                     $info['mpeg']['group_of_pictures'][] = $GOPheader;
                 }
                 break;
             case 0xc0:
                 // audio stream
             // audio stream
             case 0xc1:
                 // audio stream
             // audio stream
             case 0xc2:
                 // audio stream
             // audio stream
             case 0xc3:
                 // audio stream
             // audio stream
             case 0xc4:
                 // audio stream
             // audio stream
             case 0xc5:
                 // audio stream
             // audio stream
             case 0xc6:
                 // audio stream
             // audio stream
             case 0xc7:
                 // audio stream
             // audio stream
             case 0xc8:
                 // audio stream
             // audio stream
             case 0xc9:
                 // audio stream
             // audio stream
             case 0xca:
                 // audio stream
             // audio stream
             case 0xcb:
                 // audio stream
             // audio stream
             case 0xcc:
                 // audio stream
             // audio stream
             case 0xcd:
                 // audio stream
             // audio stream
             case 0xce:
                 // audio stream
             // audio stream
             case 0xcf:
                 // audio stream
             // audio stream
             case 0xd0:
                 // audio stream
             // audio stream
             case 0xd1:
                 // audio stream
             // audio stream
             case 0xd2:
                 // audio stream
             // audio stream
             case 0xd3:
                 // audio stream
             // audio stream
             case 0xd4:
                 // audio stream
             // audio stream
             case 0xd5:
                 // audio stream
             // audio stream
             case 0xd6:
                 // audio stream
             // audio stream
             case 0xd7:
                 // audio stream
             // audio stream
             case 0xd8:
                 // audio stream
             // audio stream
             case 0xd9:
                 // audio stream
             // audio stream
             case 0xda:
                 // audio stream
             // audio stream
             case 0xdb:
                 // audio stream
             // audio stream
             case 0xdc:
                 // audio stream
             // audio stream
             case 0xdd:
                 // audio stream
             // audio stream
             case 0xde:
                 // audio stream
             // audio stream
             case 0xdf:
                 // audio stream
                 //case 0xE0: // video stream
                 //case 0xE1: // video stream
                 //case 0xE2: // video stream
                 //case 0xE3: // video stream
                 //case 0xE4: // video stream
                 //case 0xE5: // video stream
                 //case 0xE6: // video stream
                 //case 0xE7: // video stream
                 //case 0xE8: // video stream
                 //case 0xE9: // video stream
                 //case 0xEA: // video stream
                 //case 0xEB: // video stream
                 //case 0xEC: // video stream
                 //case 0xED: // video stream
                 //case 0xEE: // video stream
                 //case 0xEF: // video stream
                 if (isset($ParsedAVchannels[$StartCodeValue])) {
                     break;
                 }
                 $ParsedAVchannels[$StartCodeValue] = $StartCodeValue;
                 // http://en.wikipedia.org/wiki/Packetized_elementary_stream
                 // http://dvd.sourceforge.net/dvdinfo/pes-hdr.html
                 /*
                 					$PackedElementaryStream = array();
                 					if ($StartCodeValue >= 0xE0) {
                 						$PackedElementaryStream['stream_type'] = 'video';
                 						$PackedElementaryStream['stream_id']   = $StartCodeValue - 0xE0;
                 					} else {
                 						$PackedElementaryStream['stream_type'] = 'audio';
                 						$PackedElementaryStream['stream_id']   = $StartCodeValue - 0xC0;
                 					}
                 					$PackedElementaryStream['packet_length'] = getid3_lib::BigEndian2Int(substr($MPEGstreamData, $StartCodeOffset + 4, 2));
                 
                 					$bitstream = getid3_lib::BigEndian2Bin(substr($MPEGstreamData, $StartCodeOffset + 6, 3)); // more may be needed below
                 					$bitstreamoffset = 0;
                 
                 					$PackedElementaryStream['marker_bits']               = self::readBitsFromStream($bitstream, $bitstreamoffset,  2); //  2 bits for marker_bits -- should be "10" = 2
                 echo 'marker_bits = '.$PackedElementaryStream['marker_bits'].'<br>';
                 					$PackedElementaryStream['scrambling_control']        = self::readBitsFromStream($bitstream, $bitstreamoffset,  2); //  2 bits for scrambling_control -- 00 implies not scrambled
                 					$PackedElementaryStream['priority']                  = self::readBitsFromStream($bitstream, $bitstreamoffset,  1); //  1 bit flag: priority
                 					$PackedElementaryStream['data_alignment_indicator']  = self::readBitsFromStream($bitstream, $bitstreamoffset,  1); //  1 bit flag: data_alignment_indicator -- 1 indicates that the PES packet header is immediately followed by the video start code or audio syncword
                 					$PackedElementaryStream['copyright']                 = self::readBitsFromStream($bitstream, $bitstreamoffset,  1); //  1 bit flag: copyright -- 1 implies copyrighted
                 					$PackedElementaryStream['original_or_copy']          = self::readBitsFromStream($bitstream, $bitstreamoffset,  1); //  1 bit flag: original_or_copy -- 1 implies original
                 					$PackedElementaryStream['pts_flag']                  = self::readBitsFromStream($bitstream, $bitstreamoffset,  1); //  1 bit flag: pts_flag -- Presentation Time Stamp
                 					$PackedElementaryStream['dts_flag']                  = self::readBitsFromStream($bitstream, $bitstreamoffset,  1); //  1 bit flag: dts_flag -- Decode Time Stamp
                 					$PackedElementaryStream['escr_flag']                 = self::readBitsFromStream($bitstream, $bitstreamoffset,  1); //  1 bit flag: escr_flag -- Elementary Stream Clock Reference
                 					$PackedElementaryStream['es_rate_flag']              = self::readBitsFromStream($bitstream, $bitstreamoffset,  1); //  1 bit flag: es_rate_flag -- Elementary Stream [data] Rate
                 					$PackedElementaryStream['dsm_trick_mode_flag']       = self::readBitsFromStream($bitstream, $bitstreamoffset,  1); //  1 bit flag: dsm_trick_mode_flag -- DSM trick mode - not used by DVD
                 					$PackedElementaryStream['additional_copy_info_flag'] = self::readBitsFromStream($bitstream, $bitstreamoffset,  1); //  1 bit flag: additional_copy_info_flag
                 					$PackedElementaryStream['crc_flag']                  = self::readBitsFromStream($bitstream, $bitstreamoffset,  1); //  1 bit flag: crc_flag
                 					$PackedElementaryStream['extension_flag']            = self::readBitsFromStream($bitstream, $bitstreamoffset,  1); //  1 bit flag: extension_flag
                 					$PackedElementaryStream['pes_remain_header_length']  = self::readBitsFromStream($bitstream, $bitstreamoffset,  8); //  1 bit flag: priority
                 
                 					$additional_header_bytes = 0;
                 					$additional_header_bytes += ($PackedElementaryStream['pts_flag']                  ? 5 : 0);
                 					$additional_header_bytes += ($PackedElementaryStream['dts_flag']                  ? 5 : 0);
                 					$additional_header_bytes += ($PackedElementaryStream['escr_flag']                 ? 6 : 0);
                 					$additional_header_bytes += ($PackedElementaryStream['es_rate_flag']              ? 3 : 0);
                 					$additional_header_bytes += ($PackedElementaryStream['additional_copy_info_flag'] ? 1 : 0);
                 					$additional_header_bytes += ($PackedElementaryStream['crc_flag']                  ? 2 : 0);
                 					$additional_header_bytes += ($PackedElementaryStream['extension_flag']            ? 1 : 0);
                 $PackedElementaryStream['additional_header_bytes'] = $additional_header_bytes;
                 					$bitstream .= getid3_lib::BigEndian2Bin(substr($MPEGstreamData, $StartCodeOffset + 9, $additional_header_bytes));
                 
                 					$info['mpeg']['packed_elementary_streams'][$PackedElementaryStream['stream_type']][$PackedElementaryStream['stream_id']][] = $PackedElementaryStream;
                 */
                 $getid3_temp = new getID3();
                 $getid3_temp->openfile($this->getid3->filename);
                 $getid3_temp->info = $info;
                 $getid3_mp3 = new getid3_mp3($getid3_temp);
                 for ($i = 0; $i <= 7; $i++) {
                     // some files have the MPEG-audio header 8 bytes after the end of the $00 $00 $01 $C0 signature, some have it up to 13 bytes (or more?) after
                     // I have no idea why or what the difference is, so this is a stupid hack.
                     // If anybody has any better idea of what's going on, please let me know - info@getid3.org
                     $getid3_temp->info = $info;
                     // only overwrite real data if valid header found
                     //echo 'audio at? '.($MPEGstreamBaseOffset + $StartCodeOffset + 4 + 8 + $i).'<br>';
                     if ($getid3_mp3->decodeMPEGaudioHeader($MPEGstreamBaseOffset + $StartCodeOffset + 4 + 8 + $i, $getid3_temp->info, false)) {
                         //echo 'yes!<br>';
                         $info = $getid3_temp->info;
                         $info['audio']['bitrate_mode'] = 'cbr';
                         $info['audio']['lossless'] = false;
                         break;
                     }
                 }
                 unset($getid3_temp, $getid3_mp3);
                 break;
             case 0xbc:
                 // Program Stream Map
             // Program Stream Map
             case 0xbd:
                 // Private stream 1 (non MPEG audio, subpictures)
             // Private stream 1 (non MPEG audio, subpictures)
             case 0xbe:
                 // Padding stream
             // Padding stream
             case 0xbf:
                 // Private stream 2 (navigation data)
             // Private stream 2 (navigation data)
             case 0xf0:
                 // ECM stream
             // ECM stream
             case 0xf1:
                 // EMM stream
             // EMM stream
             case 0xf2:
                 // DSM-CC stream
             // DSM-CC stream
             case 0xf3:
                 // ISO/IEC_13522_stream
             // ISO/IEC_13522_stream
             case 0xf4:
                 // ITU-I Rec. H.222.1 type A
             // ITU-I Rec. H.222.1 type A
             case 0xf5:
                 // ITU-I Rec. H.222.1 type B
             // ITU-I Rec. H.222.1 type B
             case 0xf6:
                 // ITU-I Rec. H.222.1 type C
             // ITU-I Rec. H.222.1 type C
             case 0xf7:
                 // ITU-I Rec. H.222.1 type D
             // ITU-I Rec. H.222.1 type D
             case 0xf8:
                 // ITU-I Rec. H.222.1 type E
             // ITU-I Rec. H.222.1 type E
             case 0xf9:
                 // ancilliary stream
             // ancilliary stream
             case 0xfa:
                 // ISO/IEC 14496-1 SL-packtized stream
             // ISO/IEC 14496-1 SL-packtized stream
             case 0xfb:
                 // ISO/IEC 14496-1 FlexMux stream
             // ISO/IEC 14496-1 FlexMux stream
             case 0xfc:
                 // metadata stream
             // metadata stream
             case 0xfd:
                 // extended stream ID
             // extended stream ID
             case 0xfe:
                 // reserved data stream
             // reserved data stream
             case 0xff:
                 // program stream directory
                 // ignore
                 break;
             default:
                 // ignore
                 break;
         }
     } while (true);
     //		// Temporary hack to account for interleaving overhead:
     //		if (!empty($info['video']['bitrate']) && !empty($info['audio']['bitrate'])) {
     //			$info['playtime_seconds'] = (($info['avdataend'] - $info['avdataoffset']) * 8) / ($info['video']['bitrate'] + $info['audio']['bitrate']);
     //
     //			// Interleaved MPEG audio/video files have a certain amount of overhead that varies
     //			// by both video and audio bitrates, and not in any sensible, linear/logarithmic pattern
     //			// Use interpolated lookup tables to approximately guess how much is overhead, because
     //			// playtime is calculated as filesize / total-bitrate
     //			$info['playtime_seconds'] *= self::systemNonOverheadPercentage($info['video']['bitrate'], $info['audio']['bitrate']);
     //
     //			//switch ($info['video']['bitrate']) {
     //			//	case('5000000'):
     //			//		$multiplier = 0.93292642112380355828048824319889;
     //			//		break;
     //			//	case('5500000'):
     //			//		$multiplier = 0.93582895375200989965359777343219;
     //			//		break;
     //			//	case('6000000'):
     //			//		$multiplier = 0.93796247714820932532911373859139;
     //			//		break;
     //			//	case('7000000'):
     //			//		$multiplier = 0.9413264083635103463010117778776;
     //			//		break;
     //			//	default:
     //			//		$multiplier = 1;
     //			//		break;
     //			//}
     //			//$info['playtime_seconds'] *= $multiplier;
     //			//$info['warning'][] = 'Interleaved MPEG audio/video playtime may be inaccurate. With current hack should be within a few seconds of accurate. Report to info@getid3.org if off by more than 10 seconds.';
     //			if ($info['video']['bitrate'] < 50000) {
     //				$this->warning('Interleaved MPEG audio/video playtime may be slightly inaccurate for video bitrates below 100kbps. Except in extreme low-bitrate situations, error should be less than 1%. Report to info@getid3.org if greater than this.');
     //			}
     //		}
     //
     /*
     $time_prev = 0;
     $byte_prev = 0;
     $vbr_bitrates = array();
     foreach ($info['mpeg']['group_of_pictures'] as $gopkey => $gopdata) {
     	$time_this = ($gopdata['time_code_hours'] * 3600) + ($gopdata['time_code_minutes'] * 60) + $gopdata['time_code_seconds'] + ($gopdata['time_code_seconds'] / 30);
     	$byte_this = $gopdata['byte_offset'];
     	if ($gopkey > 0) {
     		if ($time_this > $time_prev) {
     			$bytedelta = $byte_this - $byte_prev;
     			$timedelta = $time_this - $time_prev;
     			$this_bitrate = ($bytedelta * 8) / $timedelta;
     echo $gopkey.': ('.number_format($time_prev, 2).'-'.number_format($time_this, 2).') '.number_format($bytedelta).' bytes over '.number_format($timedelta, 3).' seconds = '.number_format($this_bitrate / 1000, 2).'kbps<br>';
     			$time_prev = $time_this;
     			$byte_prev = $byte_this;
     			$vbr_bitrates[] = $this_bitrate;
     		}
     	}
     }
     echo 'average_File_bitrate = '.number_format(array_sum($vbr_bitrates) / count($vbr_bitrates), 1).'<br>';
     */
     //echo '<pre>'.print_r($FramesByGOP, true).'</pre>';
     if ($info['mpeg']['video']['bitrate_mode'] == 'vbr') {
         $last_GOP_id = max(array_keys($FramesByGOP));
         $frames_in_last_GOP = count($FramesByGOP[$last_GOP_id]);
         $gopdata =& $info['mpeg']['group_of_pictures'][$last_GOP_id];
         $info['playtime_seconds'] = $gopdata['time_code_hours'] * 3600 + $gopdata['time_code_minutes'] * 60 + $gopdata['time_code_seconds'] + ($gopdata['time_code_pictures'] + $frames_in_last_GOP + 1) / $info['mpeg']['video']['frame_rate'];
         if (!isset($info['video']['bitrate'])) {
             $overall_bitrate = ($info['avdataend'] - $info['avdataoffset']) * 8 / $info['playtime_seconds'];
             $info['video']['bitrate'] = $overall_bitrate - (isset($info['audio']['bitrate']) ? $info['audio']['bitrate'] : 0);
         }
         unset($info['mpeg']['group_of_pictures']);
     }
     return true;
 }
Esempio n. 13
0
 public function Analyze()
 {
     $getid3 = $this->getid3;
     $getid3->info['dts'] = array();
     $info_dts =& $getid3->info['dts'];
     $getid3->info['fileformat'] = 'dts';
     fseek($getid3->fp, $getid3->info['avdataoffset'], SEEK_SET);
     $header = fread($getid3->fp, 16);
     $fhBS = getid3_lib::BigEndian2Bin(substr($header, 4, 12));
     $bs_offset = 0;
     $info_dts['raw']['frame_type'] = bindec(substr($fhBS, $bs_offset, 1));
     $bs_offset += 1;
     $info_dts['raw']['deficit_samples'] = bindec(substr($fhBS, $bs_offset, 5));
     $bs_offset += 5;
     $info_dts['flags']['crc_present'] = (bool) bindec(substr($fhBS, $bs_offset, 1));
     $bs_offset += 1;
     $info_dts['raw']['pcm_sample_blocks'] = bindec(substr($fhBS, $bs_offset, 7));
     $bs_offset += 7;
     $info_dts['raw']['frame_byte_size'] = bindec(substr($fhBS, $bs_offset, 14));
     $bs_offset += 14;
     $info_dts['raw']['channel_arrangement'] = bindec(substr($fhBS, $bs_offset, 6));
     $bs_offset += 6;
     $info_dts['raw']['sample_frequency'] = bindec(substr($fhBS, $bs_offset, 4));
     $bs_offset += 4;
     $info_dts['raw']['bitrate'] = bindec(substr($fhBS, $bs_offset, 5));
     $bs_offset += 5;
     $info_dts['flags']['embedded_downmix'] = (bool) bindec(substr($fhBS, $bs_offset, 1));
     $bs_offset += 1;
     $info_dts['flags']['dynamicrange'] = (bool) bindec(substr($fhBS, $bs_offset, 1));
     $bs_offset += 1;
     $info_dts['flags']['timestamp'] = (bool) bindec(substr($fhBS, $bs_offset, 1));
     $bs_offset += 1;
     $info_dts['flags']['auxdata'] = (bool) bindec(substr($fhBS, $bs_offset, 1));
     $bs_offset += 1;
     $info_dts['flags']['hdcd'] = (bool) bindec(substr($fhBS, $bs_offset, 1));
     $bs_offset += 1;
     $info_dts['raw']['extension_audio'] = bindec(substr($fhBS, $bs_offset, 3));
     $bs_offset += 3;
     $info_dts['flags']['extended_coding'] = (bool) bindec(substr($fhBS, $bs_offset, 1));
     $bs_offset += 1;
     $info_dts['flags']['audio_sync_insertion'] = (bool) bindec(substr($fhBS, $bs_offset, 1));
     $bs_offset += 1;
     $info_dts['raw']['lfe_effects'] = bindec(substr($fhBS, $bs_offset, 2));
     $bs_offset += 2;
     $info_dts['flags']['predictor_history'] = (bool) bindec(substr($fhBS, $bs_offset, 1));
     $bs_offset += 1;
     if ($info_dts['flags']['crc_present']) {
         $info_dts['raw']['crc16'] = bindec(substr($fhBS, $bs_offset, 16));
         $bs_offset += 16;
     }
     $info_dts['flags']['mri_perfect_reconst'] = (bool) bindec(substr($fhBS, $bs_offset, 1));
     $bs_offset += 1;
     $info_dts['raw']['encoder_soft_version'] = bindec(substr($fhBS, $bs_offset, 4));
     $bs_offset += 4;
     $info_dts['raw']['copy_history'] = bindec(substr($fhBS, $bs_offset, 2));
     $bs_offset += 2;
     $info_dts['raw']['bits_per_sample'] = bindec(substr($fhBS, $bs_offset, 2));
     $bs_offset += 2;
     $info_dts['flags']['surround_es'] = (bool) bindec(substr($fhBS, $bs_offset, 1));
     $bs_offset += 1;
     $info_dts['flags']['front_sum_diff'] = (bool) bindec(substr($fhBS, $bs_offset, 1));
     $bs_offset += 1;
     $info_dts['flags']['surround_sum_diff'] = (bool) bindec(substr($fhBS, $bs_offset, 1));
     $bs_offset += 1;
     $info_dts['raw']['dialog_normalization'] = bindec(substr($fhBS, $bs_offset, 4));
     $bs_offset += 4;
     $info_dts['bitrate'] = $this->DTSbitrateLookup($info_dts['raw']['bitrate']);
     $info_dts['bits_per_sample'] = $this->DTSbitPerSampleLookup($info_dts['raw']['bits_per_sample']);
     $info_dts['sample_rate'] = $this->DTSsampleRateLookup($info_dts['raw']['sample_frequency']);
     $info_dts['dialog_normalization'] = $this->DTSdialogNormalization($info_dts['raw']['dialog_normalization'], $info_dts['raw']['encoder_soft_version']);
     $info_dts['flags']['lossless'] = $info_dts['raw']['bitrate'] == 31 ? true : false;
     $info_dts['bitrate_mode'] = $info_dts['raw']['bitrate'] == 30 ? 'vbr' : 'cbr';
     $info_dts['channels'] = $this->DTSnumChannelsLookup($info_dts['raw']['channel_arrangement']);
     $info_dts['channel_arrangement'] = $this->DTSchannelArrangementLookup($info_dts['raw']['channel_arrangement']);
     $getid3->info['audio']['dataformat'] = 'dts';
     $getid3->info['audio']['lossless'] = $info_dts['flags']['lossless'];
     $getid3->info['audio']['bitrate_mode'] = $info_dts['bitrate_mode'];
     $getid3->info['audio']['bits_per_sample'] = $info_dts['bits_per_sample'];
     $getid3->info['audio']['sample_rate'] = $info_dts['sample_rate'];
     $getid3->info['audio']['channels'] = $info_dts['channels'];
     $getid3->info['audio']['bitrate'] = $info_dts['bitrate'];
     $getid3->info['playtime_seconds'] = ($getid3->info['avdataend'] - $getid3->info['avdataoffset']) / ($info_dts['bitrate'] / 8);
     return true;
 }
Esempio n. 14
0
 private function parseSTREAMINFO($BlockData)
 {
     $info =& $this->getid3->info;
     $info['flac']['STREAMINFO'] = array();
     $streaminfo =& $info['flac']['STREAMINFO'];
     $streaminfo['min_block_size'] = getid3_lib::BigEndian2Int(substr($BlockData, 0, 2));
     $streaminfo['max_block_size'] = getid3_lib::BigEndian2Int(substr($BlockData, 2, 2));
     $streaminfo['min_frame_size'] = getid3_lib::BigEndian2Int(substr($BlockData, 4, 3));
     $streaminfo['max_frame_size'] = getid3_lib::BigEndian2Int(substr($BlockData, 7, 3));
     $SRCSBSS = getid3_lib::BigEndian2Bin(substr($BlockData, 10, 8));
     $streaminfo['sample_rate'] = getid3_lib::Bin2Dec(substr($SRCSBSS, 0, 20));
     $streaminfo['channels'] = getid3_lib::Bin2Dec(substr($SRCSBSS, 20, 3)) + 1;
     $streaminfo['bits_per_sample'] = getid3_lib::Bin2Dec(substr($SRCSBSS, 23, 5)) + 1;
     $streaminfo['samples_stream'] = getid3_lib::Bin2Dec(substr($SRCSBSS, 28, 36));
     $streaminfo['audio_signature'] = substr($BlockData, 18, 16);
     if (!empty($streaminfo['sample_rate'])) {
         $info['audio']['bitrate_mode'] = 'vbr';
         $info['audio']['sample_rate'] = $streaminfo['sample_rate'];
         $info['audio']['channels'] = $streaminfo['channels'];
         $info['audio']['bits_per_sample'] = $streaminfo['bits_per_sample'];
         $info['playtime_seconds'] = $streaminfo['samples_stream'] / $streaminfo['sample_rate'];
         if ($info['playtime_seconds'] > 0) {
             if (!$this->isDependencyFor('matroska')) {
                 $info['audio']['bitrate'] = ($info['avdataend'] - $info['avdataoffset']) * 8 / $info['playtime_seconds'];
             } else {
                 $this->warning('Cannot determine audio bitrate because total stream size is unknown');
             }
         }
     } else {
         return $this->error('Corrupt METAdata block: STREAMINFO');
     }
     return true;
 }
Esempio n. 15
0
 public function Analyze()
 {
     $info =& $this->getid3->info;
     $info['fileformat'] = 'dts';
     $this->fseek($info['avdataoffset']);
     $DTSheader = $this->fread(20);
     // we only need 2 words magic + 6 words frame header, but these words may be normal 16-bit words OR 14-bit words with 2 highest bits set to zero, so 8 words can be either 8*16/8 = 16 bytes OR 8*16*(16/14)/8 = 18.3 bytes
     // check syncword
     $sync = substr($DTSheader, 0, 4);
     if (($encoding = array_search($sync, self::$syncwords)) !== false) {
         $info['dts']['raw']['magic'] = $sync;
         $this->readBinDataOffset = 32;
     } elseif ($this->isDependencyFor('matroska')) {
         // Matroska contains DTS without syncword encoded as raw big-endian format
         $encoding = 0;
         $this->readBinDataOffset = 0;
     } else {
         unset($info['fileformat']);
         return $this->error('Expecting "' . implode('| ', array_map('getid3_lib::PrintHexBytes', self::$syncwords)) . '" at offset ' . $info['avdataoffset'] . ', found "' . getid3_lib::PrintHexBytes($sync) . '"');
     }
     // decode header
     $fhBS = '';
     for ($word_offset = 0; $word_offset <= strlen($DTSheader); $word_offset += 2) {
         switch ($encoding) {
             case 0:
                 // raw big-endian
                 $fhBS .= getid3_lib::BigEndian2Bin(substr($DTSheader, $word_offset, 2));
                 break;
             case 1:
                 // raw little-endian
                 $fhBS .= getid3_lib::BigEndian2Bin(strrev(substr($DTSheader, $word_offset, 2)));
                 break;
             case 2:
                 // 14-bit big-endian
                 $fhBS .= substr(getid3_lib::BigEndian2Bin(substr($DTSheader, $word_offset, 2)), 2, 14);
                 break;
             case 3:
                 // 14-bit little-endian
                 $fhBS .= substr(getid3_lib::BigEndian2Bin(strrev(substr($DTSheader, $word_offset, 2))), 2, 14);
                 break;
         }
     }
     $info['dts']['raw']['frame_type'] = $this->readBinData($fhBS, 1);
     $info['dts']['raw']['deficit_samples'] = $this->readBinData($fhBS, 5);
     $info['dts']['flags']['crc_present'] = (bool) $this->readBinData($fhBS, 1);
     $info['dts']['raw']['pcm_sample_blocks'] = $this->readBinData($fhBS, 7);
     $info['dts']['raw']['frame_byte_size'] = $this->readBinData($fhBS, 14);
     $info['dts']['raw']['channel_arrangement'] = $this->readBinData($fhBS, 6);
     $info['dts']['raw']['sample_frequency'] = $this->readBinData($fhBS, 4);
     $info['dts']['raw']['bitrate'] = $this->readBinData($fhBS, 5);
     $info['dts']['flags']['embedded_downmix'] = (bool) $this->readBinData($fhBS, 1);
     $info['dts']['flags']['dynamicrange'] = (bool) $this->readBinData($fhBS, 1);
     $info['dts']['flags']['timestamp'] = (bool) $this->readBinData($fhBS, 1);
     $info['dts']['flags']['auxdata'] = (bool) $this->readBinData($fhBS, 1);
     $info['dts']['flags']['hdcd'] = (bool) $this->readBinData($fhBS, 1);
     $info['dts']['raw']['extension_audio'] = $this->readBinData($fhBS, 3);
     $info['dts']['flags']['extended_coding'] = (bool) $this->readBinData($fhBS, 1);
     $info['dts']['flags']['audio_sync_insertion'] = (bool) $this->readBinData($fhBS, 1);
     $info['dts']['raw']['lfe_effects'] = $this->readBinData($fhBS, 2);
     $info['dts']['flags']['predictor_history'] = (bool) $this->readBinData($fhBS, 1);
     if ($info['dts']['flags']['crc_present']) {
         $info['dts']['raw']['crc16'] = $this->readBinData($fhBS, 16);
     }
     $info['dts']['flags']['mri_perfect_reconst'] = (bool) $this->readBinData($fhBS, 1);
     $info['dts']['raw']['encoder_soft_version'] = $this->readBinData($fhBS, 4);
     $info['dts']['raw']['copy_history'] = $this->readBinData($fhBS, 2);
     $info['dts']['raw']['bits_per_sample'] = $this->readBinData($fhBS, 2);
     $info['dts']['flags']['surround_es'] = (bool) $this->readBinData($fhBS, 1);
     $info['dts']['flags']['front_sum_diff'] = (bool) $this->readBinData($fhBS, 1);
     $info['dts']['flags']['surround_sum_diff'] = (bool) $this->readBinData($fhBS, 1);
     $info['dts']['raw']['dialog_normalization'] = $this->readBinData($fhBS, 4);
     $info['dts']['bitrate'] = self::bitrateLookup($info['dts']['raw']['bitrate']);
     $info['dts']['bits_per_sample'] = self::bitPerSampleLookup($info['dts']['raw']['bits_per_sample']);
     $info['dts']['sample_rate'] = self::sampleRateLookup($info['dts']['raw']['sample_frequency']);
     $info['dts']['dialog_normalization'] = self::dialogNormalization($info['dts']['raw']['dialog_normalization'], $info['dts']['raw']['encoder_soft_version']);
     $info['dts']['flags']['lossless'] = $info['dts']['raw']['bitrate'] == 31 ? true : false;
     $info['dts']['bitrate_mode'] = $info['dts']['raw']['bitrate'] == 30 ? 'vbr' : 'cbr';
     $info['dts']['channels'] = self::numChannelsLookup($info['dts']['raw']['channel_arrangement']);
     $info['dts']['channel_arrangement'] = self::channelArrangementLookup($info['dts']['raw']['channel_arrangement']);
     $info['audio']['dataformat'] = 'dts';
     $info['audio']['lossless'] = $info['dts']['flags']['lossless'];
     $info['audio']['bitrate_mode'] = $info['dts']['bitrate_mode'];
     $info['audio']['bits_per_sample'] = $info['dts']['bits_per_sample'];
     $info['audio']['sample_rate'] = $info['dts']['sample_rate'];
     $info['audio']['channels'] = $info['dts']['channels'];
     $info['audio']['bitrate'] = $info['dts']['bitrate'];
     if (isset($info['avdataend']) && !empty($info['dts']['bitrate']) && is_numeric($info['dts']['bitrate'])) {
         $info['playtime_seconds'] = ($info['avdataend'] - $info['avdataoffset']) / ($info['dts']['bitrate'] / 8);
         if ($encoding == 2 || $encoding == 3) {
             // 14-bit data packed into 16-bit words, so the playtime is wrong because only (14/16) of the bytes in the data portion of the file are used at the specified bitrate
             $info['playtime_seconds'] *= 14 / 16;
         }
     }
     return true;
 }
Esempio n. 16
0
 static function FixedPoint2_30($rawdata)
 {
     $binarystring = getid3_lib::BigEndian2Bin($rawdata);
     return getid3_lib::Bin2Dec(substr($binarystring, 0, 2)) + (double) (getid3_lib::Bin2Dec(substr($binarystring, 2, 30)) / 1073741824);
 }
 public function Analyze()
 {
     $getid3 = $this->getid3;
     // http://faac.sourceforge.net/wiki/index.php?page=ADIF
     // http://libmpeg.org/mpeg4/doc/w2203tfs.pdf
     // adif_header() {
     //     adif_id                                32
     //     copyright_id_present                    1
     //     if( copyright_id_present )
     //         copyright_id                       72
     //     original_copy                           1
     //     home                                    1
     //     bitstream_type                          1
     //     bitrate                                23
     //     num_program_config_elements             4
     //     for (i = 0; i < num_program_config_elements + 1; i++ ) {
     //         if( bitstream_type == '0' )
     //             adif_buffer_fullness           20
     //         program_config_element()
     //     }
     // }
     $getid3->info['fileformat'] = 'aac';
     $getid3->info['audio']['dataformat'] = 'aac';
     $getid3->info['audio']['lossless'] = false;
     $getid3->info['aac']['header'] = array();
     $info_aac =& $getid3->info['aac'];
     $info_aac_header =& $info_aac['header'];
     fseek($getid3->fp, $getid3->info['avdataoffset'], SEEK_SET);
     $aac_header_bitstream = getid3_lib::BigEndian2Bin(fread($getid3->fp, 1024));
     $info_aac['header_type'] = 'ADIF';
     $info_aac_header['mpeg_version'] = 4;
     $bit_offset = 32;
     $info_aac_header['copyright'] = $aac_header_bitstream[$bit_offset++] == '1';
     if ($info_aac_header['copyright']) {
         $info_aac_header['copyright_id'] = getid3_aac_adif::Bin2String(substr($aac_header_bitstream, $bit_offset, 72));
         $bit_offset += 72;
     }
     $info_aac_header['original_copy'] = $aac_header_bitstream[$bit_offset++] == '1';
     $info_aac_header['home'] = $aac_header_bitstream[$bit_offset++] == '1';
     $info_aac_header['is_vbr'] = $aac_header_bitstream[$bit_offset++] == '1';
     if ($info_aac_header['is_vbr']) {
         $getid3->info['audio']['bitrate_mode'] = 'vbr';
         $info_aac_header['bitrate_max'] = bindec(substr($aac_header_bitstream, $bit_offset, 23));
         $bit_offset += 23;
     } else {
         $getid3->info['audio']['bitrate_mode'] = 'cbr';
         $info_aac_header['bitrate'] = bindec(substr($aac_header_bitstream, $bit_offset, 23));
         $bit_offset += 23;
         $getid3->info['audio']['bitrate'] = $info_aac_header['bitrate'];
     }
     $info_aac_header['num_program_configs'] = 1 + bindec(substr($aac_header_bitstream, $bit_offset, 4));
     $bit_offset += 4;
     for ($i = 0; $i < $info_aac_header['num_program_configs']; $i++) {
         // http://www.audiocoding.com/wiki/index.php?page=program_config_element
         // buffer_fullness                       20
         // element_instance_tag                   4
         // object_type                            2
         // sampling_frequency_index               4
         // num_front_channel_elements             4
         // num_side_channel_elements              4
         // num_back_channel_elements              4
         // num_lfe_channel_elements               2
         // num_assoc_data_elements                3
         // num_valid_cc_elements                  4
         // mono_mixdown_present                   1
         // mono_mixdown_element_number            4   if mono_mixdown_present == 1
         // stereo_mixdown_present                 1
         // stereo_mixdown_element_number          4   if stereo_mixdown_present == 1
         // matrix_mixdown_idx_present             1
         // matrix_mixdown_idx                     2   if matrix_mixdown_idx_present == 1
         // pseudo_surround_enable                 1   if matrix_mixdown_idx_present == 1
         // for (i = 0; i < num_front_channel_elements; i++) {
         //     front_element_is_cpe[i]            1
         //     front_element_tag_select[i]        4
         // }
         // for (i = 0; i < num_side_channel_elements; i++) {
         //     side_element_is_cpe[i]             1
         //     side_element_tag_select[i]         4
         // }
         // for (i = 0; i < num_back_channel_elements; i++) {
         //     back_element_is_cpe[i]             1
         //     back_element_tag_select[i]         4
         // }
         // for (i = 0; i < num_lfe_channel_elements; i++) {
         //     lfe_element_tag_select[i]          4
         // }
         // for (i = 0; i < num_assoc_data_elements; i++) {
         //     assoc_data_element_tag_select[i]   4
         // }
         // for (i = 0; i < num_valid_cc_elements; i++) {
         //     cc_element_is_ind_sw[i]            1
         //     valid_cc_element_tag_select[i]     4
         // }
         // byte_alignment()                       VAR
         // comment_field_bytes                    8
         // for (i = 0; i < comment_field_bytes; i++) {
         //     comment_field_data[i]              8
         // }
         $info_aac['program_configs'][$i] = array();
         $info_aac_program_configs_i =& $info_aac['program_configs'][$i];
         if (!$info_aac_header['is_vbr']) {
             $info_aac_program_configs_i['buffer_fullness'] = bindec(substr($aac_header_bitstream, $bit_offset, 20));
             $bit_offset += 20;
         }
         $info_aac_program_configs_i['element_instance_tag'] = bindec(substr($aac_header_bitstream, $bit_offset, 4));
         $info_aac_program_configs_i['object_type'] = bindec(substr($aac_header_bitstream, $bit_offset + 4, 2));
         $info_aac_program_configs_i['sampling_frequency_index'] = bindec(substr($aac_header_bitstream, $bit_offset + 6, 4));
         $info_aac_program_configs_i['num_front_channel_elements'] = bindec(substr($aac_header_bitstream, $bit_offset + 10, 4));
         $info_aac_program_configs_i['num_side_channel_elements'] = bindec(substr($aac_header_bitstream, $bit_offset + 14, 4));
         $info_aac_program_configs_i['num_back_channel_elements'] = bindec(substr($aac_header_bitstream, $bit_offset + 18, 4));
         $info_aac_program_configs_i['num_lfe_channel_elements'] = bindec(substr($aac_header_bitstream, $bit_offset + 22, 2));
         $info_aac_program_configs_i['num_assoc_data_elements'] = bindec(substr($aac_header_bitstream, $bit_offset + 24, 3));
         $info_aac_program_configs_i['num_valid_cc_elements'] = bindec(substr($aac_header_bitstream, $bit_offset + 27, 4));
         $bit_offset += 31;
         $info_aac_program_configs_i['mono_mixdown_present'] = $aac_header_bitstream[$bit_offset++] == 1;
         if ($info_aac_program_configs_i['mono_mixdown_present']) {
             $info_aac_program_configs_i['mono_mixdown_element_number'] = bindec(substr($aac_header_bitstream, $bit_offset, 4));
             $bit_offset += 4;
         }
         $info_aac_program_configs_i['stereo_mixdown_present'] = $aac_header_bitstream[$bit_offset++] == 1;
         if ($info_aac_program_configs_i['stereo_mixdown_present']) {
             $info_aac_program_configs_i['stereo_mixdown_element_number'] = bindec(substr($aac_header_bitstream, $bit_offset, 4));
             $bit_offset += 4;
         }
         $info_aac_program_configs_i['matrix_mixdown_idx_present'] = $aac_header_bitstream[$bit_offset++] == 1;
         if ($info_aac_program_configs_i['matrix_mixdown_idx_present']) {
             $info_aac_program_configs_i['matrix_mixdown_idx'] = bindec(substr($aac_header_bitstream, $bit_offset, 2));
             $bit_offset += 2;
             $info_aac_program_configs_i['pseudo_surround_enable'] = $aac_header_bitstream[$bit_offset++] == 1;
         }
         for ($j = 0; $j < $info_aac_program_configs_i['num_front_channel_elements']; $j++) {
             $info_aac_program_configs_i['front_element_is_cpe'][$j] = $aac_header_bitstream[$bit_offset++] == 1;
             $info_aac_program_configs_i['front_element_tag_select'][$j] = bindec(substr($aac_header_bitstream, $bit_offset, 4));
             $bit_offset += 4;
         }
         for ($j = 0; $j < $info_aac_program_configs_i['num_side_channel_elements']; $j++) {
             $info_aac_program_configs_i['side_element_is_cpe'][$j] = $aac_header_bitstream[$bit_offset++] == 1;
             $info_aac_program_configs_i['side_element_tag_select'][$j] = bindec(substr($aac_header_bitstream, $bit_offset, 4));
             $bit_offset += 4;
         }
         for ($j = 0; $j < $info_aac_program_configs_i['num_back_channel_elements']; $j++) {
             $info_aac_program_configs_i['back_element_is_cpe'][$j] = $aac_header_bitstream[$bit_offset++] == 1;
             $info_aac_program_configs_i['back_element_tag_select'][$j] = bindec(substr($aac_header_bitstream, $bit_offset, 4));
             $bit_offset += 4;
         }
         for ($j = 0; $j < $info_aac_program_configs_i['num_lfe_channel_elements']; $j++) {
             $info_aac_program_configs_i['lfe_element_tag_select'][$j] = bindec(substr($aac_header_bitstream, $bit_offset, 4));
             $bit_offset += 4;
         }
         for ($j = 0; $j < $info_aac_program_configs_i['num_assoc_data_elements']; $j++) {
             $info_aac_program_configs_i['assoc_data_element_tag_select'][$j] = bindec(substr($aac_header_bitstream, $bit_offset, 4));
             $bit_offset += 4;
         }
         for ($j = 0; $j < $info_aac_program_configs_i['num_valid_cc_elements']; $j++) {
             $info_aac_program_configs_i['cc_element_is_ind_sw'][$j] = $aac_header_bitstream[$bit_offset++] == 1;
             $info_aac_program_configs_i['valid_cc_element_tag_select'][$j] = bindec(substr($aac_header_bitstream, $bit_offset, 4));
             $bit_offset += 4;
         }
         $bit_offset = ceil($bit_offset / 8) * 8;
         $info_aac_program_configs_i['comment_field_bytes'] = bindec(substr($aac_header_bitstream, $bit_offset, 8));
         $bit_offset += 8;
         $info_aac_program_configs_i['comment_field'] = getid3_aac_adif::Bin2String(substr($aac_header_bitstream, $bit_offset, 8 * $info_aac_program_configs_i['comment_field_bytes']));
         $bit_offset += 8 * $info_aac_program_configs_i['comment_field_bytes'];
         $info_aac_header['profile_text'] = getid3_aac_adif::AACprofileLookup($info_aac_program_configs_i['object_type'], $info_aac_header['mpeg_version']);
         $info_aac_program_configs_i['sampling_frequency'] = $getid3->info['audio']['sample_rate'] = getid3_aac_adif::AACsampleRateLookup($info_aac_program_configs_i['sampling_frequency_index']);
         $getid3->info['audio']['channels'] = getid3_aac_adif::AACchannelCountCalculate($info_aac_program_configs_i);
         if ($info_aac_program_configs_i['comment_field']) {
             $info_aac['comments'][] = $info_aac_program_configs_i['comment_field'];
         }
     }
     $getid3->info['playtime_seconds'] = ($getid3->info['avdataend'] - $getid3->info['avdataoffset']) * 8 / $getid3->info['audio']['bitrate'];
     $getid3->info['audio']['encoder_options'] = $info_aac['header_type'] . ' ' . $info_aac_header['profile_text'];
     return true;
 }