Exemplo n.º 1
0
 function ParseRIFF(&$fd, $startoffset, $maxoffset, &$ThisFileInfo)
 {
     $maxoffset = min($maxoffset, $ThisFileInfo['avdataend']);
     $RIFFchunk = false;
     fseek($fd, $startoffset, SEEK_SET);
     while (ftell($fd) < $maxoffset) {
         $chunkname = fread($fd, 4);
         if (strlen($chunkname) < 4) {
             $ThisFileInfo['error'][] = 'Expecting chunk name at offset ' . (ftell($fd) - 4) . ' but found nothing. Aborting RIFF parsing.';
             break;
         }
         $chunksize = getid3_riff::EitherEndian2Int($ThisFileInfo, fread($fd, 4));
         if ($chunksize == 0) {
             $ThisFileInfo['error'][] = 'Chunk size at offset ' . (ftell($fd) - 4) . ' is zero. Aborting RIFF parsing.';
             break;
         }
         if ($chunksize % 2 != 0) {
             // all structures are packed on word boundaries
             $chunksize++;
         }
         switch ($chunkname) {
             case 'LIST':
                 $listname = fread($fd, 4);
                 switch ($listname) {
                     case 'movi':
                     case 'rec ':
                         $RIFFchunk[$listname]['offset'] = ftell($fd) - 4;
                         $RIFFchunk[$listname]['size'] = $chunksize;
                         static $ParsedAudioStream = false;
                         if ($ParsedAudioStream) {
                             // skip over
                         } else {
                             $WhereWeWere = ftell($fd);
                             $AudioChunkHeader = fread($fd, 12);
                             $AudioChunkStreamNum = substr($AudioChunkHeader, 0, 2);
                             $AudioChunkStreamType = substr($AudioChunkHeader, 2, 2);
                             $AudioChunkSize = getid3_lib::LittleEndian2Int(substr($AudioChunkHeader, 4, 4));
                             if ($AudioChunkStreamType == 'wb') {
                                 $FirstFourBytes = substr($AudioChunkHeader, 8, 4);
                                 if (preg_match('/^\\xFF[\\xE2-\\xE7\\xF2-\\xF7\\xFA-\\xFF][\\x00-\\xEB]/s', $FirstFourBytes)) {
                                     // MP3
                                     if (getid3_mp3::MPEGaudioHeaderBytesValid($FirstFourBytes)) {
                                         $dummy = $ThisFileInfo;
                                         $dummy['avdataoffset'] = ftell($fd) - 4;
                                         $dummy['avdataend'] = ftell($fd) + $AudioChunkSize;
                                         getid3_mp3::getOnlyMPEGaudioInfo($fd, $dummy, $dummy['avdataoffset'], false);
                                         if (isset($dummy['mpeg']['audio'])) {
                                             $ThisFileInfo = $dummy;
                                             $ThisFileInfo['audio']['dataformat'] = 'mp' . $ThisFileInfo['mpeg']['audio']['layer'];
                                             $ThisFileInfo['audio']['sample_rate'] = $ThisFileInfo['mpeg']['audio']['sample_rate'];
                                             $ThisFileInfo['audio']['channels'] = $ThisFileInfo['mpeg']['audio']['channels'];
                                             $ThisFileInfo['audio']['bitrate'] = $ThisFileInfo['mpeg']['audio']['bitrate'];
                                             $ThisFileInfo['bitrate'] = $ThisFileInfo['audio']['bitrate'];
                                             $ThisFileInfo['audio']['bitrate_mode'] = strtolower($ThisFileInfo['mpeg']['audio']['bitrate_mode']);
                                         }
                                     }
                                 } elseif (preg_match('/^\\x0B\\x77/s', $FirstFourBytes)) {
                                     // AC3
                                     $GETID3_ERRORARRAY =& $ThisFileInfo['warning'];
                                     if (getid3_lib::IncludeDependency(GETID3_INCLUDEPATH . 'module.audio.ac3.php', __FILE__, false)) {
                                         $dummy = $ThisFileInfo;
                                         $dummy['avdataoffset'] = ftell($fd) - 4;
                                         $dummy['avdataend'] = ftell($fd) + $AudioChunkSize;
                                         $dummy['error'] = array();
                                         $ac3_tag = new getid3_ac3($fd, $dummy);
                                         if (empty($dummy['error'])) {
                                             $ThisFileInfo['audio'] = $dummy['audio'];
                                             $ThisFileInfo['ac3'] = $dummy['ac3'];
                                             $ThisFileInfo['warning'] = $dummy['warning'];
                                         }
                                     }
                                 }
                             }
                             $ParsedAudioStream = true;
                             fseek($fd, $WhereWeWere, SEEK_SET);
                         }
                         fseek($fd, $chunksize - 4, SEEK_CUR);
                         break;
                     default:
                         if (!isset($RIFFchunk[$listname])) {
                             $RIFFchunk[$listname] = array();
                         }
                         $LISTchunkParent = $listname;
                         $LISTchunkMaxOffset = ftell($fd) - 4 + $chunksize;
                         if ($parsedChunk = getid3_riff::ParseRIFF($fd, ftell($fd), ftell($fd) + $chunksize - 4, $ThisFileInfo)) {
                             $RIFFchunk[$listname] = array_merge_recursive($RIFFchunk[$listname], $parsedChunk);
                         }
                         break;
                 }
                 break;
             default:
                 $thisindex = 0;
                 if (isset($RIFFchunk[$chunkname]) && is_array($RIFFchunk[$chunkname])) {
                     $thisindex = count($RIFFchunk[$chunkname]);
                 }
                 $RIFFchunk[$chunkname][$thisindex]['offset'] = ftell($fd) - 8;
                 $RIFFchunk[$chunkname][$thisindex]['size'] = $chunksize;
                 switch ($chunkname) {
                     case 'data':
                         $ThisFileInfo['avdataoffset'] = ftell($fd);
                         $ThisFileInfo['avdataend'] = $ThisFileInfo['avdataoffset'] + $chunksize;
                         $RIFFdataChunkContentsTest = fread($fd, 36);
                         if (strlen($RIFFdataChunkContentsTest) > 0 && preg_match('/^\\xFF[\\xE2-\\xE7\\xF2-\\xF7\\xFA-\\xFF][\\x00-\\xEB]/s', substr($RIFFdataChunkContentsTest, 0, 4))) {
                             // Probably is MP3 data
                             if (getid3_mp3::MPEGaudioHeaderBytesValid(substr($RIFFdataChunkContentsTest, 0, 4))) {
                                 getid3_mp3::getOnlyMPEGaudioInfo($fd, $ThisFileInfo, $RIFFchunk[$chunkname][$thisindex]['offset'], false);
                             }
                         } elseif (strlen($RIFFdataChunkContentsTest) > 0 && substr($RIFFdataChunkContentsTest, 0, 2) == "\vw") {
                             // This is probably AC-3 data
                             $GETID3_ERRORARRAY =& $ThisFileInfo['warning'];
                             if (getid3_lib::IncludeDependency(GETID3_INCLUDEPATH . 'module.audio.ac3.php', __FILE__, false)) {
                                 $dummy = $ThisFileInfo;
                                 $dummy['avdataoffset'] = $RIFFchunk[$chunkname][$thisindex]['offset'];
                                 $dummy['avdataend'] = $dummy['avdataoffset'] + $RIFFchunk[$chunkname][$thisindex]['size'];
                                 $dummy['error'] = array();
                                 $ac3_tag = new getid3_ac3($fd, $dummy);
                                 if (empty($dummy['error'])) {
                                     $ThisFileInfo['audio'] = $dummy['audio'];
                                     $ThisFileInfo['ac3'] = $dummy['ac3'];
                                     $ThisFileInfo['warning'] = $dummy['warning'];
                                 }
                             }
                         } elseif (strlen($RIFFdataChunkContentsTest) > 0 && substr($RIFFdataChunkContentsTest, 8, 2) == "w\v") {
                             // Dolby Digital WAV
                             // AC-3 content, but not encoded in same format as normal AC-3 file
                             // For one thing, byte order is swapped
                             $GETID3_ERRORARRAY =& $ThisFileInfo['warning'];
                             if (getid3_lib::IncludeDependency(GETID3_INCLUDEPATH . 'module.audio.ac3.php', __FILE__, false)) {
                                 // ok to use tmpfile here - only 56 bytes
                                 if ($fd_temp = tmpfile()) {
                                     for ($i = 0; $i < 28; $i += 2) {
                                         // swap byte order
                                         fwrite($fd_temp, substr($RIFFdataChunkContentsTest, 8 + $i + 1, 1));
                                         fwrite($fd_temp, substr($RIFFdataChunkContentsTest, 8 + $i + 0, 1));
                                     }
                                     $dummy = $ThisFileInfo;
                                     $dummy['avdataoffset'] = 0;
                                     $dummy['avdataend'] = 20;
                                     $dummy['error'] = array();
                                     $ac3_tag = new getid3_ac3($fd_temp, $dummy);
                                     fclose($fd_temp);
                                     if (empty($dummy['error'])) {
                                         $ThisFileInfo['audio'] = $dummy['audio'];
                                         $ThisFileInfo['ac3'] = $dummy['ac3'];
                                         $ThisFileInfo['warning'] = $dummy['warning'];
                                     } else {
                                         $ThisFileInfo['error'][] = 'Errors parsing DolbyDigital WAV: ' . explode(';', $dummy['error']);
                                     }
                                 } else {
                                     $ThisFileInfo['error'][] = 'Could not create temporary file to analyze DolbyDigital WAV';
                                 }
                             }
                         } elseif (strlen($RIFFdataChunkContentsTest) > 0 && substr($RIFFdataChunkContentsTest, 0, 4) == 'wvpk') {
                             // This is WavPack data
                             $ThisFileInfo['wavpack']['offset'] = $RIFFchunk[$chunkname][$thisindex]['offset'];
                             $ThisFileInfo['wavpack']['size'] = getid3_lib::LittleEndian2Int(substr($RIFFdataChunkContentsTest, 4, 4));
                             getid3_riff::RIFFparseWavPackHeader(substr($RIFFdataChunkContentsTest, 8, 28), $ThisFileInfo);
                         } else {
                             // This is some other kind of data (quite possibly just PCM)
                             // do nothing special, just skip it
                         }
                         fseek($fd, $RIFFchunk[$chunkname][$thisindex]['offset'] + 8 + $chunksize, SEEK_SET);
                         break;
                     case 'bext':
                     case 'cart':
                     case 'fmt ':
                     case 'MEXT':
                     case 'DISP':
                         // always read data in
                         $RIFFchunk[$chunkname][$thisindex]['data'] = fread($fd, $chunksize);
                         break;
                     default:
                         if (!empty($LISTchunkParent) && $RIFFchunk[$chunkname][$thisindex]['offset'] + $RIFFchunk[$chunkname][$thisindex]['size'] <= $LISTchunkMaxOffset) {
                             $RIFFchunk[$LISTchunkParent][$chunkname][$thisindex]['offset'] = $RIFFchunk[$chunkname][$thisindex]['offset'];
                             $RIFFchunk[$LISTchunkParent][$chunkname][$thisindex]['size'] = $RIFFchunk[$chunkname][$thisindex]['size'];
                             unset($RIFFchunk[$chunkname][$thisindex]['offset']);
                             unset($RIFFchunk[$chunkname][$thisindex]['size']);
                             if (isset($RIFFchunk[$chunkname][$thisindex]) && empty($RIFFchunk[$chunkname][$thisindex])) {
                                 unset($RIFFchunk[$chunkname][$thisindex]);
                             }
                             if (isset($RIFFchunk[$chunkname]) && empty($RIFFchunk[$chunkname])) {
                                 unset($RIFFchunk[$chunkname]);
                             }
                             $RIFFchunk[$LISTchunkParent][$chunkname][$thisindex]['data'] = fread($fd, $chunksize);
                         } elseif ($chunksize < 2048) {
                             // only read data in if smaller than 2kB
                             $RIFFchunk[$chunkname][$thisindex]['data'] = fread($fd, $chunksize);
                         } else {
                             fseek($fd, $chunksize, SEEK_CUR);
                         }
                         break;
                 }
                 break;
         }
     }
     return $RIFFchunk;
 }
 function Analyze()
 {
     $info =& $this->getid3->info;
     // http://www.matroska.org/technical/specs/index.html#EBMLBasics
     $offset = $info['avdataoffset'];
     $EBMLdata = '';
     $EBMLdata_offset = $offset;
     if (!getid3_lib::intValueSupported($info['avdataend'])) {
         $this->warnings[] = 'This version of getID3() [' . $this->getid3->version() . '] may or may not correctly handle Matroska files larger than ' . round(PHP_INT_MAX / 1073741824) . 'GB';
     }
     while ($offset < $info['avdataend']) {
         $this->EnsureBufferHasEnoughData($EBMLdata, $offset, $EBMLdata_offset);
         $top_element_offset = $offset;
         $top_element_id = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
         $top_element_length = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
         if ($top_element_length === false) {
             $this->warnings[] = 'invalid chunk length at ' . $top_element_offset;
             $offset = PHP_INT_MAX + 1;
             break;
         }
         $top_element_endoffset = $offset + $top_element_length;
         switch ($top_element_id) {
             case EBML_ID_EBML:
                 $info['fileformat'] = 'matroska';
                 $info['matroska']['header']['offset'] = $top_element_offset;
                 $info['matroska']['header']['length'] = $top_element_length;
                 while ($offset < $top_element_endoffset) {
                     $this->EnsureBufferHasEnoughData($EBMLdata, $offset, $EBMLdata_offset);
                     $element_data = array();
                     $element_data_offset = $offset;
                     $element_data['id'] = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
                     $element_data['id_name'] = $this->EBMLidName($element_data['id']);
                     $element_data['length'] = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
                     $end_offset = $offset + $element_data['length'];
                     switch ($element_data['id']) {
                         case EBML_ID_VOID:
                             // padding, ignore
                             break;
                         case EBML_ID_EBMLVERSION:
                         case EBML_ID_EBMLREADVERSION:
                         case EBML_ID_EBMLMAXIDLENGTH:
                         case EBML_ID_EBMLMAXSIZELENGTH:
                         case EBML_ID_DOCTYPEVERSION:
                         case EBML_ID_DOCTYPEREADVERSION:
                             $element_data['data'] = getid3_lib::BigEndian2Int(substr($EBMLdata, $offset - $EBMLdata_offset, $element_data['length']));
                             break;
                         case EBML_ID_DOCTYPE:
                             $element_data['data'] = trim(substr($EBMLdata, $offset - $EBMLdata_offset, $element_data['length']), "");
                             break;
                         case EBML_ID_CRC32:
                             // probably not useful, ignore
                             unset($element_data);
                             break;
                         default:
                             $this->warnings[] = 'Unhandled track.video element [' . basename(__FILE__) . ':' . __LINE__ . '] (' . $element_data['id'] . '::' . $element_data['id_name'] . ') at ' . $element_data_offset;
                             break;
                     }
                     $offset = $end_offset;
                     if (!empty($element_data)) {
                         $info['matroska']['header']['elements'][] = $element_data;
                     }
                 }
                 break;
             case EBML_ID_SEGMENT:
                 $info['matroska']['segment'][0]['offset'] = $top_element_offset;
                 $info['matroska']['segment'][0]['length'] = $top_element_length;
                 $segment_key = -1;
                 while ($offset < $info['avdataend']) {
                     $this->EnsureBufferHasEnoughData($EBMLdata, $offset, $EBMLdata_offset);
                     $element_data = array();
                     $element_data['offset'] = $offset;
                     $element_data['id'] = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
                     $element_data['id_name'] = $this->EBMLidName($element_data['id']);
                     $element_data['length'] = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
                     if ($element_data['length'] === false) {
                         $this->warnings[] = 'invalid chunk length at ' . $element_data['offset'];
                         //$offset = PHP_INT_MAX + 1;
                         $offset = $info['avdataend'];
                         break;
                     }
                     $element_end = $offset + $element_data['length'];
                     switch ($element_data['id']) {
                         //case EBML_ID_CLUSTER:
                         //	// too many cluster entries, probably not useful
                         //	break;
                         case false:
                             $this->warnings[] = 'invalid ID at ' . $element_data['offset'];
                             $offset = $element_end;
                             continue 3;
                         default:
                             $info['matroska']['segments'][] = $element_data;
                             break;
                     }
                     $segment_key++;
                     switch ($element_data['id']) {
                         case EBML_ID_SEEKHEAD:
                             // Contains the position of other level 1 elements
                             while ($offset < $element_end) {
                                 $this->EnsureBufferHasEnoughData($EBMLdata, $offset, $EBMLdata_offset);
                                 $seek_entry = array();
                                 $seek_entry['offset'] = $offset;
                                 $seek_entry['id'] = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
                                 $seek_entry['id_name'] = $this->EBMLidName($seek_entry['id']);
                                 $seek_entry['length'] = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
                                 $seek_end_offset = $offset + $seek_entry['length'];
                                 switch ($seek_entry['id']) {
                                     case EBML_ID_SEEK:
                                         // Contains a single seek entry to an EBML element
                                         while ($offset < $seek_end_offset) {
                                             $this->EnsureBufferHasEnoughData($EBMLdata, $offset, $EBMLdata_offset);
                                             $id = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
                                             $length = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
                                             $value = substr($EBMLdata, $offset - $EBMLdata_offset, $length);
                                             $offset += $length;
                                             switch ($id) {
                                                 case EBML_ID_SEEKID:
                                                     $dummy = 0;
                                                     $seek_entry['target_id'] = $this->readEBMLint($value, $dummy);
                                                     $seek_entry['target_name'] = $this->EBMLidName($seek_entry['target_id']);
                                                     break;
                                                 case EBML_ID_SEEKPOSITION:
                                                     $seek_entry['target_offset'] = $element_data['offset'] + getid3_lib::BigEndian2Int($value);
                                                     break;
                                                 case EBML_ID_CRC32:
                                                     // probably not useful, ignore
                                                     //$seek_entry['crc32'] = getid3_lib::PrintHexBytes($value, true, false, false);
                                                     unset($seek_entry);
                                                     break;
                                                 default:
                                                     $info['error'][] = 'Unhandled segment [' . basename(__FILE__) . ':' . __LINE__ . '] (' . $id . ') at ' . $offset;
                                                     break;
                                             }
                                         }
                                         if (!empty($seek_entry)) {
                                             $info['matroska']['seek'][] = $seek_entry;
                                         }
                                         //switch ($seek_entry['target_id']) {
                                         //	case EBML_ID_CLUSTER:
                                         //		// too many cluster seek points, probably not useful
                                         //		break;
                                         //	default:
                                         //		$info['matroska']['seek'][] = $seek_entry;
                                         //		break;
                                         //}
                                         break;
                                     case EBML_ID_CRC32:
                                         // probably not useful, ignore
                                         break;
                                     default:
                                         $this->warnings[] = 'Unhandled seekhead element [' . basename(__FILE__) . ':' . __LINE__ . '] (' . $seek_entry['id'] . '::' . $seek_entry['id_name'] . ') at ' . $offset;
                                         break;
                                 }
                                 $offset = $seek_end_offset;
                             }
                             break;
                         case EBML_ID_TRACKS:
                             // information about all tracks in segment
                             $info['matroska']['tracks'] = $element_data;
                             while ($offset < $element_end) {
                                 $this->EnsureBufferHasEnoughData($EBMLdata, $offset, $EBMLdata_offset);
                                 $track_entry = array();
                                 $track_entry['offset'] = $offset;
                                 $track_entry['id'] = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
                                 $track_entry['id_name'] = $this->EBMLidName($track_entry['id']);
                                 $track_entry['length'] = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
                                 $track_entry_endoffset = $offset + $track_entry['length'];
                                 // $track_entry['offset'] is not the same as $offset, even though they were set equal a few lines up: $offset has been automagically incremented by readEMLint()
                                 switch ($track_entry['id']) {
                                     case EBML_ID_TRACKENTRY:
                                         //subelements: Describes a track with all elements.
                                         while ($offset < $track_entry_endoffset) {
                                             $this->EnsureBufferHasEnoughData($EBMLdata, $offset, $EBMLdata_offset);
                                             $subelement_offset = $offset;
                                             $subelement_id = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
                                             $subelement_idname = $this->EBMLidName($subelement_id);
                                             $subelement_length = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
                                             $subelement_end = $offset + $subelement_length;
                                             switch ($subelement_id) {
                                                 case EBML_ID_TRACKNUMBER:
                                                 case EBML_ID_TRACKUID:
                                                 case EBML_ID_TRACKTYPE:
                                                 case EBML_ID_MINCACHE:
                                                 case EBML_ID_MAXCACHE:
                                                 case EBML_ID_MAXBLOCKADDITIONID:
                                                 case EBML_ID_DEFAULTDURATION:
                                                     // nanoseconds per frame
                                                     $track_entry[$subelement_idname] = getid3_lib::BigEndian2Int(substr($EBMLdata, $offset - $EBMLdata_offset, $subelement_length));
                                                     break;
                                                 case EBML_ID_TRACKTIMECODESCALE:
                                                     $track_entry[$subelement_idname] = getid3_lib::BigEndian2Float(substr($EBMLdata, $offset - $EBMLdata_offset, $subelement_length));
                                                     break;
                                                 case EBML_ID_CODECID:
                                                 case EBML_ID_LANGUAGE:
                                                 case EBML_ID_NAME:
                                                 case EBML_ID_CODECNAME:
                                                 case EBML_ID_CODECPRIVATE:
                                                     $track_entry[$subelement_idname] = trim(substr($EBMLdata, $offset - $EBMLdata_offset, $subelement_length), "");
                                                     break;
                                                     // thought maybe it was a nice wFormatTag entry, but it's not :(
                                                     //case EBML_ID_CODECPRIVATE:
                                                     //$track_entry[$subelement_idname] =                             trim(substr($EBMLdata, $offset - $EBMLdata_offset, $subelement_length), "\x00");
                                                     //if (getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio-video.riff.php', __FILE__, false)) {
                                                     //	$track_entry[$subelement_idname.'_decoded'] = getid3_riff::RIFFparseWAVEFORMATex($track_entry[$subelement_idname]);
                                                     //	if (isset($track_entry[$subelement_idname.'_decoded']['raw']['wFormatTag'])) {
                                                     //	}
                                                     //} else {
                                                     //	$this->warnings[] = 'failed to include "module.audio-video.riff.php" for parsing codec private data';
                                                     //}
                                                     //break;
                                                 // thought maybe it was a nice wFormatTag entry, but it's not :(
                                                 //case EBML_ID_CODECPRIVATE:
                                                 //$track_entry[$subelement_idname] =                             trim(substr($EBMLdata, $offset - $EBMLdata_offset, $subelement_length), "\x00");
                                                 //if (getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio-video.riff.php', __FILE__, false)) {
                                                 //	$track_entry[$subelement_idname.'_decoded'] = getid3_riff::RIFFparseWAVEFORMATex($track_entry[$subelement_idname]);
                                                 //	if (isset($track_entry[$subelement_idname.'_decoded']['raw']['wFormatTag'])) {
                                                 //	}
                                                 //} else {
                                                 //	$this->warnings[] = 'failed to include "module.audio-video.riff.php" for parsing codec private data';
                                                 //}
                                                 //break;
                                                 case EBML_ID_FLAGENABLED:
                                                 case EBML_ID_FLAGDEFAULT:
                                                 case EBML_ID_FLAGFORCED:
                                                 case EBML_ID_FLAGLACING:
                                                 case EBML_ID_CODECDECODEALL:
                                                     $track_entry[$subelement_idname] = (bool) getid3_lib::BigEndian2Int(substr($EBMLdata, $offset - $EBMLdata_offset, $subelement_length));
                                                     break;
                                                 case EBML_ID_VIDEO:
                                                     while ($offset < $subelement_end) {
                                                         $this->EnsureBufferHasEnoughData($EBMLdata, $offset, $EBMLdata_offset);
                                                         $sub_subelement_offset = $offset;
                                                         $sub_subelement_id = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
                                                         $sub_subelement_idname = $this->EBMLidName($sub_subelement_id);
                                                         $sub_subelement_length = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
                                                         $sub_subelement_end = $offset + $sub_subelement_length;
                                                         switch ($sub_subelement_id) {
                                                             case EBML_ID_PIXELWIDTH:
                                                             case EBML_ID_PIXELHEIGHT:
                                                             case EBML_ID_STEREOMODE:
                                                             case EBML_ID_PIXELCROPBOTTOM:
                                                             case EBML_ID_PIXELCROPTOP:
                                                             case EBML_ID_PIXELCROPLEFT:
                                                             case EBML_ID_PIXELCROPRIGHT:
                                                             case EBML_ID_DISPLAYWIDTH:
                                                             case EBML_ID_DISPLAYHEIGHT:
                                                             case EBML_ID_DISPLAYUNIT:
                                                             case EBML_ID_ASPECTRATIOTYPE:
                                                                 $track_entry[$sub_subelement_idname] = getid3_lib::BigEndian2Int(substr($EBMLdata, $offset - $EBMLdata_offset, $sub_subelement_length));
                                                                 break;
                                                             case EBML_ID_FLAGINTERLACED:
                                                                 $track_entry[$sub_subelement_idname] = (bool) getid3_lib::BigEndian2Int(substr($EBMLdata, $offset - $EBMLdata_offset, $sub_subelement_length));
                                                                 break;
                                                             case EBML_ID_GAMMAVALUE:
                                                                 $track_entry[$sub_subelement_idname] = getid3_lib::BigEndian2Float(substr($EBMLdata, $offset - $EBMLdata_offset, $sub_subelement_length));
                                                                 break;
                                                             case EBML_ID_COLOURSPACE:
                                                                 $track_entry[$sub_subelement_idname] = trim(substr($EBMLdata, $offset - $EBMLdata_offset, $sub_subelement_length), "");
                                                                 break;
                                                             default:
                                                                 $this->warnings[] = 'Unhandled track.video element [' . basename(__FILE__) . ':' . __LINE__ . '] (' . $sub_subelement_id . '::' . $sub_subelement_idname . ') at ' . $sub_subelement_offset;
                                                                 break;
                                                         }
                                                         $offset = $sub_subelement_end;
                                                     }
                                                     if (isset($track_entry[$this->EBMLidName(EBML_ID_CODECID)]) && $track_entry[$this->EBMLidName(EBML_ID_CODECID)] == 'V_MS/VFW/FOURCC' && isset($track_entry[$this->EBMLidName(EBML_ID_CODECPRIVATE)])) {
                                                         if (getid3_lib::IncludeDependency(GETID3_INCLUDEPATH . 'module.audio-video.riff.php', __FILE__, false)) {
                                                             $track_entry['codec_private_parsed'] = getid3_riff::ParseBITMAPINFOHEADER($track_entry[$this->EBMLidName(EBML_ID_CODECPRIVATE)]);
                                                         } else {
                                                             $this->warnings[] = 'Unable to parse codec private data [' . basename(__FILE__) . ':' . __LINE__ . '] because cannot include "module.audio-video.riff.php"';
                                                         }
                                                     }
                                                     break;
                                                 case EBML_ID_AUDIO:
                                                     while ($offset < $subelement_end) {
                                                         $this->EnsureBufferHasEnoughData($EBMLdata, $offset, $EBMLdata_offset);
                                                         $sub_subelement_offset = $offset;
                                                         $sub_subelement_id = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
                                                         $sub_subelement_idname = $this->EBMLidName($sub_subelement_id);
                                                         $sub_subelement_length = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
                                                         $sub_subelement_end = $offset + $sub_subelement_length;
                                                         switch ($sub_subelement_id) {
                                                             case EBML_ID_CHANNELS:
                                                             case EBML_ID_BITDEPTH:
                                                                 $track_entry[$sub_subelement_idname] = getid3_lib::BigEndian2Int(substr($EBMLdata, $offset - $EBMLdata_offset, $sub_subelement_length));
                                                                 break;
                                                             case EBML_ID_SAMPLINGFREQUENCY:
                                                             case EBML_ID_OUTPUTSAMPLINGFREQUENCY:
                                                                 $track_entry[$sub_subelement_idname] = getid3_lib::BigEndian2Float(substr($EBMLdata, $offset - $EBMLdata_offset, $sub_subelement_length));
                                                                 break;
                                                             case EBML_ID_CHANNELPOSITIONS:
                                                                 $track_entry[$sub_subelement_idname] = trim(substr($EBMLdata, $offset - $EBMLdata_offset, $sub_subelement_length), "");
                                                                 break;
                                                             default:
                                                                 $this->warnings[] = 'Unhandled track.audio element [' . basename(__FILE__) . ':' . __LINE__ . '] (' . $sub_subelement_id . '::' . $sub_subelement_idname . ') at ' . $sub_subelement_offset;
                                                                 break;
                                                         }
                                                         $offset = $sub_subelement_end;
                                                     }
                                                     break;
                                                 case EBML_ID_CONTENTENCODINGS:
                                                     while ($offset < $subelement_end) {
                                                         $this->EnsureBufferHasEnoughData($EBMLdata, $offset, $EBMLdata_offset);
                                                         $sub_subelement_offset = $offset;
                                                         $sub_subelement_id = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
                                                         $sub_subelement_idname = $this->EBMLidName($sub_subelement_id);
                                                         $sub_subelement_length = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
                                                         $sub_subelement_end = $offset + $sub_subelement_length;
                                                         switch ($sub_subelement_id) {
                                                             case EBML_ID_CONTENTENCODING:
                                                                 while ($offset < $sub_subelement_end) {
                                                                     $this->EnsureBufferHasEnoughData($EBMLdata, $offset, $EBMLdata_offset);
                                                                     $sub_sub_subelement_offset = $offset;
                                                                     $sub_sub_subelement_id = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
                                                                     $sub_sub_subelement_idname = $this->EBMLidName($sub_sub_subelement_id);
                                                                     $sub_sub_subelement_length = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
                                                                     $sub_sub_subelement_end = $offset + $sub_sub_subelement_length;
                                                                     switch ($sub_sub_subelement_id) {
                                                                         case EBML_ID_CONTENTENCODINGORDER:
                                                                         case EBML_ID_CONTENTENCODINGSCOPE:
                                                                         case EBML_ID_CONTENTENCODINGTYPE:
                                                                             $track_entry[$sub_subelement_idname][$sub_sub_subelement_idname] = getid3_lib::BigEndian2Int(substr($EBMLdata, $offset - $EBMLdata_offset, $sub_sub_subelement_length));
                                                                             break;
                                                                         case EBML_ID_CONTENTCOMPRESSION:
                                                                             while ($offset < $sub_sub_subelement_end) {
                                                                                 $this->EnsureBufferHasEnoughData($EBMLdata, $offset, $EBMLdata_offset);
                                                                                 $sub_sub_sub_subelement_offset = $offset;
                                                                                 $sub_sub_sub_subelement_id = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
                                                                                 $sub_sub_sub_subelement_idname = $this->EBMLidName($sub_sub_subelement_id);
                                                                                 $sub_sub_sub_subelement_length = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
                                                                                 $sub_sub_sub_subelement_end = $offset + $sub_sub_sub_subelement_length;
                                                                                 switch ($sub_sub_sub_subelement_id) {
                                                                                     case EBML_ID_CONTENTCOMPALGO:
                                                                                         $track_entry[$sub_subelement_idname][$sub_sub_subelement_idname][$sub_sub_sub_subelement_idname] = getid3_lib::BigEndian2Int(substr($EBMLdata, $offset - $EBMLdata_offset, $sub_sub_sub_subelement_length));
                                                                                         break;
                                                                                     case EBML_ID_CONTENTCOMPSETTINGS:
                                                                                         $track_entry[$sub_subelement_idname][$sub_sub_subelement_idname][$sub_sub_sub_subelement_idname] = substr($EBMLdata, $offset - $EBMLdata_offset, $sub_sub_sub_subelement_length);
                                                                                         break;
                                                                                     default:
                                                                                         $this->warnings[] = 'Unhandled track.contentencodings.contentencoding.contentcompression element [' . basename(__FILE__) . ':' . __LINE__ . '] (' . $subelement_id . '::' . $subelement_idname . ' [' . $subelement_length . ' bytes]) at ' . $subelement_offset;
                                                                                         break;
                                                                                 }
                                                                                 $offset = $sub_sub_sub_subelement_end;
                                                                             }
                                                                             break;
                                                                         case EBML_ID_CONTENTENCRYPTION:
                                                                             while ($offset < $sub_sub_subelement_end) {
                                                                                 $this->EnsureBufferHasEnoughData($EBMLdata, $offset, $EBMLdata_offset);
                                                                                 $sub_sub_sub_subelement_offset = $offset;
                                                                                 $sub_sub_sub_subelement_id = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
                                                                                 $sub_sub_sub_subelement_idname = $this->EBMLidName($sub_sub_subelement_id);
                                                                                 $sub_sub_sub_subelement_length = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
                                                                                 $sub_sub_sub_subelement_end = $offset + $sub_sub_sub_subelement_length;
                                                                                 switch ($sub_sub_sub_subelement_id) {
                                                                                     case EBML_ID_CONTENTENCALGO:
                                                                                     case EBML_ID_CONTENTSIGALGO:
                                                                                     case EBML_ID_CONTENTSIGHASHALGO:
                                                                                         $track_entry[$sub_subelement_idname][$sub_sub_subelement_idname][$sub_sub_sub_subelement_idname] = getid3_lib::BigEndian2Int(substr($EBMLdata, $offset - $EBMLdata_offset, $sub_sub_sub_subelement_length));
                                                                                         break;
                                                                                     case EBML_ID_CONTENTENCKEYID:
                                                                                     case EBML_ID_CONTENTSIGNATURE:
                                                                                     case EBML_ID_CONTENTSIGKEYID:
                                                                                         $track_entry[$sub_subelement_idname][$sub_sub_subelement_idname][$sub_sub_sub_subelement_idname] = substr($EBMLdata, $offset - $EBMLdata_offset, $sub_sub_sub_subelement_length);
                                                                                         break;
                                                                                     default:
                                                                                         $this->warnings[] = 'Unhandled track.contentencodings.contentencoding.contentcompression element [' . basename(__FILE__) . ':' . __LINE__ . '] (' . $subelement_id . '::' . $subelement_idname . ' [' . $subelement_length . ' bytes]) at ' . $subelement_offset;
                                                                                         break;
                                                                                 }
                                                                                 $offset = $sub_sub_sub_subelement_end;
                                                                             }
                                                                             break;
                                                                         default:
                                                                             $this->warnings[] = 'Unhandled track.contentencodings.contentencoding element [' . basename(__FILE__) . ':' . __LINE__ . '] (' . $subelement_id . '::' . $subelement_idname . ' [' . $subelement_length . ' bytes]) at ' . $subelement_offset;
                                                                             break;
                                                                     }
                                                                     $offset = $sub_sub_subelement_end;
                                                                 }
                                                                 break;
                                                             default:
                                                                 $this->warnings[] = 'Unhandled track.contentencodings element [' . basename(__FILE__) . ':' . __LINE__ . '] (' . $subelement_id . '::' . $subelement_idname . ' [' . $subelement_length . ' bytes]) at ' . $subelement_offset;
                                                                 break;
                                                         }
                                                         $offset = $sub_subelement_end;
                                                     }
                                                     break;
                                                 case EBML_ID_CRC32:
                                                     // probably not useful, ignore
                                                     break;
                                                 default:
                                                     $this->warnings[] = 'Unhandled track element [' . basename(__FILE__) . ':' . __LINE__ . '] (' . $subelement_id . '::' . $subelement_idname . ' [' . $subelement_length . ' bytes]) at ' . $subelement_offset;
                                                     break;
                                             }
                                             $offset = $subelement_end;
                                         }
                                         break;
                                     case EBML_ID_CRC32:
                                         // probably not useful, ignore
                                         $offset = $track_entry_endoffset;
                                         break;
                                     default:
                                         $this->warnings[] = 'Unhandled track element [' . basename(__FILE__) . ':' . __LINE__ . '] (' . $track_entry['id'] . '::' . $track_entry['id_name'] . ') at ' . $track_entry['offset'];
                                         $offset = $track_entry_endoffset;
                                         break;
                                 }
                                 $info['matroska']['tracks']['tracks'][] = $track_entry;
                             }
                             break;
                         case EBML_ID_INFO:
                             // Contains the position of other level 1 elements
                             $info_entry = array();
                             while ($offset < $element_end) {
                                 $this->EnsureBufferHasEnoughData($EBMLdata, $offset, $EBMLdata_offset);
                                 $subelement_offset = $offset;
                                 $subelement_id = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
                                 $subelement_idname = $this->EBMLidName($subelement_id);
                                 $subelement_length = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
                                 $subelement_end = $offset + $subelement_length;
                                 switch ($subelement_id) {
                                     case EBML_ID_CHAPTERTRANSLATEEDITIONUID:
                                     case EBML_ID_CHAPTERTRANSLATECODEC:
                                     case EBML_ID_TIMECODESCALE:
                                         $info_entry[$subelement_idname] = getid3_lib::BigEndian2Int(substr($EBMLdata, $offset - $EBMLdata_offset, $subelement_length));
                                         break;
                                     case EBML_ID_DURATION:
                                         $info_entry[$subelement_idname] = getid3_lib::BigEndian2Float(substr($EBMLdata, $offset - $EBMLdata_offset, $subelement_length));
                                         break;
                                     case EBML_ID_DATEUTC:
                                         $info_entry[$subelement_idname] = getid3_lib::BigEndian2Int(substr($EBMLdata, $offset - $EBMLdata_offset, $subelement_length));
                                         $info_entry[$subelement_idname . '_unix'] = $this->EBMLdate2unix($info_entry[$subelement_idname]);
                                         break;
                                     case EBML_ID_SEGMENTUID:
                                     case EBML_ID_PREVUID:
                                     case EBML_ID_NEXTUID:
                                     case EBML_ID_SEGMENTFAMILY:
                                     case EBML_ID_CHAPTERTRANSLATEID:
                                         $info_entry[$subelement_idname] = trim(substr($EBMLdata, $offset - $EBMLdata_offset, $subelement_length), "");
                                         break;
                                     case EBML_ID_SEGMENTFILENAME:
                                     case EBML_ID_PREVFILENAME:
                                     case EBML_ID_NEXTFILENAME:
                                     case EBML_ID_TITLE:
                                     case EBML_ID_MUXINGAPP:
                                     case EBML_ID_WRITINGAPP:
                                         $info_entry[$subelement_idname] = trim(substr($EBMLdata, $offset - $EBMLdata_offset, $subelement_length), "");
                                         $info['matroska']['comments'][strtolower($subelement_idname)][] = $info_entry[$subelement_idname];
                                         break;
                                     case EBML_ID_CRC32:
                                         // probably not useful, ignore
                                         break;
                                     default:
                                         $this->warnings[] = 'Unhandled info element [' . basename(__FILE__) . ':' . __LINE__ . '] (' . $subelement_id . '::' . $subelement_idname . ' [' . $subelement_length . ' bytes]) at ' . $subelement_offset;
                                         break;
                                 }
                                 $offset = $subelement_end;
                             }
                             $info['matroska']['info'][] = $info_entry;
                             break;
                         case EBML_ID_CUES:
                             $cues_entry = array();
                             while ($offset < $element_end) {
                                 $this->EnsureBufferHasEnoughData($EBMLdata, $offset, $EBMLdata_offset);
                                 $subelement_offset = $offset;
                                 $subelement_id = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
                                 $subelement_idname = $this->EBMLidName($subelement_id);
                                 $subelement_length = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
                                 $subelement_end = $offset + $subelement_length;
                                 switch ($subelement_id) {
                                     case EBML_ID_CUEPOINT:
                                         $cuepoint_entry = array();
                                         while ($offset < $subelement_end) {
                                             $this->EnsureBufferHasEnoughData($EBMLdata, $offset, $EBMLdata_offset);
                                             $sub_subelement_offset = $offset;
                                             $sub_subelement_id = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
                                             $sub_subelement_idname = $this->EBMLidName($sub_subelement_id);
                                             $sub_subelement_length = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
                                             $sub_subelement_end = $offset + $sub_subelement_length;
                                             switch ($sub_subelement_id) {
                                                 case EBML_ID_CUETRACKPOSITIONS:
                                                     while ($offset < $sub_subelement_end) {
                                                         $this->EnsureBufferHasEnoughData($EBMLdata, $offset, $EBMLdata_offset);
                                                         $sub_sub_subelement_offset = $offset;
                                                         $sub_sub_subelement_id = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
                                                         $sub_sub_subelement_idname = $this->EBMLidName($sub_sub_subelement_id);
                                                         $sub_sub_subelement_length = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
                                                         $sub_sub_subelement_end = $offset + $sub_sub_subelement_length;
                                                         switch ($sub_sub_subelement_id) {
                                                             case EBML_ID_CUETRACK:
                                                                 $cuepoint_entry[$sub_sub_subelement_idname] = getid3_lib::BigEndian2Int(substr($EBMLdata, $offset - $EBMLdata_offset, $sub_sub_subelement_length));
                                                                 break;
                                                             default:
                                                                 $this->warnings[] = 'Unhandled cues.cuepoint.cuetrackpositions element [' . basename(__FILE__) . ':' . __LINE__ . '] (' . $sub_sub_subelement_id . '::' . $sub_sub_subelement_idname . ') at ' . $sub_sub_subelement_offset;
                                                                 break;
                                                         }
                                                         $offset = $sub_subelement_end;
                                                     }
                                                     break;
                                                 case EBML_ID_CUETIME:
                                                     $cuepoint_entry[$subelement_idname] = getid3_lib::BigEndian2Int(substr($EBMLdata, $offset - $EBMLdata_offset, $sub_subelement_length));
                                                     break;
                                                 default:
                                                     $this->warnings[] = 'Unhandled cues.cuepoint element [' . basename(__FILE__) . ':' . __LINE__ . '] (' . $sub_subelement_id . '::' . $sub_subelement_idname . ') at ' . $sub_subelement_offset;
                                                     break;
                                             }
                                             $offset = $sub_subelement_end;
                                         }
                                         $cues_entry[] = $cuepoint_entry;
                                         $offset = $sub_subelement_end;
                                         break;
                                     case EBML_ID_CRC32:
                                         // probably not useful, ignore
                                         break;
                                     default:
                                         $this->warnings[] = 'Unhandled cues element [' . basename(__FILE__) . ':' . __LINE__ . '] (' . $subelement_id . '::' . $subelement_idname . ' [' . $subelement_length . ' bytes]) at ' . $subelement_offset;
                                         break;
                                 }
                                 $offset = $subelement_end;
                             }
                             $info['matroska']['cues'] = $cues_entry;
                             break;
                         case EBML_ID_TAGS:
                             $tags_entry = array();
                             while ($offset < $element_end) {
                                 $this->EnsureBufferHasEnoughData($EBMLdata, $offset, $EBMLdata_offset);
                                 $subelement_offset = $offset;
                                 $subelement_id = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
                                 $subelement_idname = $this->EBMLidName($subelement_id);
                                 $subelement_length = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
                                 $subelement_end = $offset + $subelement_length;
                                 $tag_entry = array();
                                 switch ($subelement_id) {
                                     case EBML_ID_WRITINGAPP:
                                         $tag_entry[$subelement_idname] = substr($EBMLdata, $offset - $EBMLdata_offset, $subelement_length);
                                         break;
                                     case EBML_ID_TAG:
                                         while ($offset < $subelement_end) {
                                             $this->EnsureBufferHasEnoughData($EBMLdata, $offset, $EBMLdata_offset);
                                             $sub_subelement_offset = $offset;
                                             $sub_subelement_id = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
                                             $sub_subelement_idname = $this->EBMLidName($sub_subelement_id);
                                             $sub_subelement_length = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
                                             $sub_subelement_end = $offset + $sub_subelement_length;
                                             switch ($sub_subelement_id) {
                                                 case EBML_ID_TARGETS:
                                                     $targets_entry = array();
                                                     while ($offset < $sub_subelement_end) {
                                                         $this->EnsureBufferHasEnoughData($EBMLdata, $offset, $EBMLdata_offset);
                                                         $sub_sub_subelement_offset = $offset;
                                                         $sub_sub_subelement_id = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
                                                         $sub_sub_subelement_idname = $this->EBMLidName($sub_sub_subelement_id);
                                                         $sub_sub_subelement_length = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
                                                         $sub_sub_subelement_end = $offset + $sub_sub_subelement_length;
                                                         switch ($sub_sub_subelement_id) {
                                                             case EBML_ID_TARGETTYPEVALUE:
                                                                 $targets_entry[$sub_sub_subelement_idname] = getid3_lib::BigEndian2Int(substr($EBMLdata, $offset - $EBMLdata_offset, $sub_sub_subelement_length));
                                                                 $targets_entry[strtolower($sub_sub_subelement_idname) . '_long'] = $this->MatroskaTargetTypeValue($targets_entry[$sub_sub_subelement_idname]);
                                                                 break;
                                                             case EBML_ID_EDITIONUID:
                                                             case EBML_ID_CHAPTERUID:
                                                             case EBML_ID_ATTACHMENTUID:
                                                             case EBML_ID_TAGTRACKUID:
                                                             case EBML_ID_TAGCHAPTERUID:
                                                                 $targets_entry[$sub_sub_subelement_idname] = getid3_lib::BigEndian2Int(substr($EBMLdata, $offset - $EBMLdata_offset, $sub_sub_subelement_length));
                                                                 break;
                                                             default:
                                                                 $this->warnings[] = 'Unhandled tag.targets element [' . basename(__FILE__) . ':' . __LINE__ . '] (' . $sub_sub_subelement_id . '::' . $sub_sub_subelement_idname . ') at ' . $sub_sub_subelement_offset;
                                                                 break;
                                                         }
                                                         $offset = $sub_sub_subelement_end;
                                                     }
                                                     $tag_entry[$sub_subelement_idname][] = $targets_entry;
                                                     break;
                                                 case EBML_ID_SIMPLETAG:
                                                     //$tag_entry[$sub_subelement_idname][] = $simpletag_entry;
                                                     $tag_entry[$sub_subelement_idname][] = $this->Handle_EMBL_ID_SIMPLETAG($offset, $sub_subelement_end);
                                                     break;
                                                 case EBML_ID_TARGETTYPE:
                                                     $tag_entry[$sub_subelement_idname] = substr($EBMLdata, $offset - $EBMLdata_offset, $sub_subelement_length);
                                                     break;
                                                 case EBML_ID_TRACKUID:
                                                     $tag_entry[$sub_subelement_idname] = getid3_lib::BigEndian2Int(substr($EBMLdata, $offset - $EBMLdata_offset, $sub_subelement_length));
                                                     break;
                                                 default:
                                                     $this->warnings[] = 'Unhandled tags.tag element [' . basename(__FILE__) . ':' . __LINE__ . '] (' . $sub_subelement_id . '::' . $sub_subelement_idname . ') at ' . $sub_subelement_offset;
                                                     break;
                                             }
                                             $offset = $sub_subelement_end;
                                         }
                                         $offset = $sub_subelement_end;
                                         break;
                                     case EBML_ID_CRC32:
                                         // probably not useful, ignore
                                         break;
                                     default:
                                         $this->warnings[] = 'Unhandled tags element [' . basename(__FILE__) . ':' . __LINE__ . '] (' . $subelement_id . '::' . $subelement_idname . ' [' . $subelement_length . ' bytes]) at ' . $subelement_offset;
                                         break;
                                 }
                                 $tags_entry['tags'][] = $tag_entry;
                                 $offset = $subelement_end;
                             }
                             $info['matroska']['tags'] = $tags_entry['tags'];
                             break;
                         case EBML_ID_ATTACHMENTS:
                             while ($offset < $element_end) {
                                 $this->EnsureBufferHasEnoughData($EBMLdata, $offset, $EBMLdata_offset);
                                 $subelement_offset = $offset;
                                 $subelement_id = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
                                 $subelement_idname = $this->EBMLidName($subelement_id);
                                 $subelement_length = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
                                 $subelement_end = $offset + $subelement_length;
                                 switch ($subelement_id) {
                                     case EBML_ID_ATTACHEDFILE:
                                         $attachedfile_entry = array();
                                         while ($offset < $subelement_end) {
                                             $this->EnsureBufferHasEnoughData($EBMLdata, $offset, $EBMLdata_offset);
                                             $sub_subelement_offset = $offset;
                                             $sub_subelement_id = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
                                             $sub_subelement_idname = $this->EBMLidName($sub_subelement_id);
                                             $sub_subelement_length = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
                                             $sub_subelement_end = $offset + $sub_subelement_length;
                                             switch ($sub_subelement_id) {
                                                 case EBML_ID_FILEDESCRIPTION:
                                                 case EBML_ID_FILENAME:
                                                 case EBML_ID_FILEMIMETYPE:
                                                     $attachedfile_entry[$sub_subelement_idname] = substr($EBMLdata, $offset - $EBMLdata_offset, $sub_subelement_length);
                                                     break;
                                                 case EBML_ID_FILEDATA:
                                                     $attachedfile_entry['data_offset'] = $offset;
                                                     $attachedfile_entry['data_length'] = $sub_subelement_length;
                                                     do {
                                                         if ($this->inline_attachments === false) {
                                                             // skip entirely
                                                             break;
                                                         }
                                                         if ($this->inline_attachments === true) {
                                                             // great
                                                         } elseif (is_int($this->inline_attachments)) {
                                                             if ($this->inline_attachments < $sub_subelement_length) {
                                                                 // too big, skip
                                                                 $this->warnings[] = 'attachment at ' . $sub_subelement_offset . ' is too large to process inline (' . number_format($sub_subelement_length) . ' bytes)';
                                                                 break;
                                                             }
                                                         } elseif (is_string($this->inline_attachments)) {
                                                             $this->inline_attachments = rtrim(str_replace(array('/', '\\'), DIRECTORY_SEPARATOR, $this->inline_attachments), DIRECTORY_SEPARATOR);
                                                             if (!is_dir($this->inline_attachments) || !is_writable($this->inline_attachments)) {
                                                                 // cannot write, skip
                                                                 $this->warnings[] = 'attachment at ' . $sub_subelement_offset . ' cannot be saved to "' . $this->inline_attachments . '" (not writable)';
                                                                 break;
                                                             }
                                                         }
                                                         // if we get this far, must be OK
                                                         $this->EnsureBufferHasEnoughData($EBMLdata, $offset, $EBMLdata_offset, $sub_subelement_length);
                                                         $attachedfile_entry[$sub_subelement_idname] = substr($EBMLdata, $offset - $EBMLdata_offset, $sub_subelement_length);
                                                         if (is_string($this->inline_attachments)) {
                                                             $destination_filename = $this->inline_attachments . DIRECTORY_SEPARATOR . md5($info['filenamepath']) . '_' . $attachedfile_entry['data_offset'];
                                                             if (!file_exists($destination_filename) || is_writable($destination_filename)) {
                                                                 file_put_contents($destination_filename, $attachedfile_entry[$sub_subelement_idname]);
                                                             } else {
                                                                 $this->warnings[] = 'attachment at ' . $sub_subelement_offset . ' cannot be saved to "' . $destination_filename . '" (not writable)';
                                                             }
                                                             $attachedfile_entry[$sub_subelement_idname . '_filename'] = $destination_filename;
                                                             unset($attachedfile_entry[$sub_subelement_idname]);
                                                         }
                                                     } while (false);
                                                     break;
                                                 case EBML_ID_FILEUID:
                                                     $attachedfile_entry[$sub_subelement_idname] = getid3_lib::BigEndian2Int(substr($EBMLdata, $offset - $EBMLdata_offset, $sub_subelement_length));
                                                     break;
                                                 default:
                                                     $this->warnings[] = 'Unhandled attachment.attachedfile element [' . basename(__FILE__) . ':' . __LINE__ . '] (' . $sub_subelement_id . '::' . $sub_subelement_idname . ') at ' . $sub_subelement_offset;
                                                     break;
                                             }
                                             $offset = $sub_subelement_end;
                                         }
                                         if (!empty($attachedfile_entry[$this->EBMLidName(EBML_ID_FILEDATA)]) && !empty($attachedfile_entry[$this->EBMLidName(EBML_ID_FILEMIMETYPE)]) && preg_match('#^image/#i', $attachedfile_entry[$this->EBMLidName(EBML_ID_FILEMIMETYPE)])) {
                                             if ($this->inline_attachments === true || is_int($this->inline_attachments) && $this->inline_attachments >= strlen($attachedfile_entry[$this->EBMLidName(EBML_ID_FILEDATA)])) {
                                                 $attachedfile_entry['data'] = $attachedfile_entry[$this->EBMLidName(EBML_ID_FILEDATA)];
                                                 $attachedfile_entry['image_mime'] = $attachedfile_entry[$this->EBMLidName(EBML_ID_FILEMIMETYPE)];
                                                 $info['matroska']['comments']['picture'][] = array('data' => $attachedfile_entry['data'], 'image_mime' => $attachedfile_entry['image_mime'], 'filename' => !empty($attachedfile_entry[$this->EBMLidName(EBML_ID_FILENAME)]) ? $attachedfile_entry[$this->EBMLidName(EBML_ID_FILENAME)] : '');
                                                 unset($attachedfile_entry[$this->EBMLidName(EBML_ID_FILEDATA)], $attachedfile_entry[$this->EBMLidName(EBML_ID_FILEMIMETYPE)]);
                                             }
                                         }
                                         if (!empty($attachedfile_entry['image_mime']) && preg_match('#^image/#i', $attachedfile_entry['image_mime'])) {
                                             // don't add a second copy of attached images, which are grouped under the standard location [comments][picture]
                                         } else {
                                             $info['matroska']['attachments'][] = $attachedfile_entry;
                                         }
                                         $offset = $sub_subelement_end;
                                         break;
                                     case EBML_ID_CRC32:
                                         // probably not useful, ignore
                                         break;
                                     default:
                                         $this->warnings[] = 'Unhandled tags element [' . basename(__FILE__) . ':' . __LINE__ . '] (' . $subelement_id . '::' . $subelement_idname . ' [' . $subelement_length . ' bytes]) at ' . $subelement_offset;
                                         break;
                                 }
                                 $offset = $subelement_end;
                             }
                             break;
                         case EBML_ID_CHAPTERS:
                             // not important to us, contains mostly actual audio/video data, ignore
                             while ($offset < $element_end) {
                                 $this->EnsureBufferHasEnoughData($EBMLdata, $offset, $EBMLdata_offset);
                                 $subelement_offset = $offset;
                                 $subelement_id = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
                                 $subelement_idname = $this->EBMLidName($subelement_id);
                                 $subelement_length = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
                                 $subelement_end = $offset + $subelement_length;
                                 switch ($subelement_id) {
                                     case EBML_ID_EDITIONENTRY:
                                         $editionentry_entry = array();
                                         while ($offset < $subelement_end) {
                                             $this->EnsureBufferHasEnoughData($EBMLdata, $offset, $EBMLdata_offset);
                                             $sub_subelement_offset = $offset;
                                             $sub_subelement_id = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
                                             $sub_subelement_idname = $this->EBMLidName($sub_subelement_id);
                                             $sub_subelement_length = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
                                             $sub_subelement_end = $offset + $sub_subelement_length;
                                             switch ($sub_subelement_id) {
                                                 case EBML_ID_EDITIONUID:
                                                     $editionentry_entry[$sub_subelement_idname] = getid3_lib::BigEndian2Int(substr($EBMLdata, $offset - $EBMLdata_offset, $sub_subelement_length));
                                                     break;
                                                 case EBML_ID_EDITIONFLAGHIDDEN:
                                                 case EBML_ID_EDITIONFLAGDEFAULT:
                                                 case EBML_ID_EDITIONFLAGORDERED:
                                                     $editionentry_entry[$sub_subelement_idname] = (bool) getid3_lib::BigEndian2Int(substr($EBMLdata, $offset - $EBMLdata_offset, $sub_subelement_length));
                                                     break;
                                                 case EBML_ID_CHAPTERATOM:
                                                     $chapteratom_entry = array();
                                                     while ($offset < $sub_subelement_end) {
                                                         $this->EnsureBufferHasEnoughData($EBMLdata, $offset, $EBMLdata_offset);
                                                         $sub_sub_subelement_offset = $offset;
                                                         $sub_sub_subelement_id = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
                                                         $sub_sub_subelement_idname = $this->EBMLidName($sub_sub_subelement_id);
                                                         $sub_sub_subelement_length = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
                                                         $sub_sub_subelement_end = $offset + $sub_sub_subelement_length;
                                                         switch ($sub_sub_subelement_id) {
                                                             case EBML_ID_CHAPTERSEGMENTUID:
                                                             case EBML_ID_CHAPTERSEGMENTEDITIONUID:
                                                                 $chapteratom_entry[$sub_sub_subelement_idname] = substr($EBMLdata, $offset - $EBMLdata_offset, $sub_sub_subelement_length);
                                                                 break;
                                                             case EBML_ID_CHAPTERFLAGENABLED:
                                                             case EBML_ID_CHAPTERFLAGHIDDEN:
                                                                 $chapteratom_entry[$sub_sub_subelement_idname] = (bool) getid3_lib::BigEndian2Int(substr($EBMLdata, $offset - $EBMLdata_offset, $sub_sub_subelement_length));
                                                                 break;
                                                             case EBML_ID_CHAPTERUID:
                                                             case EBML_ID_CHAPTERTIMESTART:
                                                             case EBML_ID_CHAPTERTIMEEND:
                                                                 $chapteratom_entry[$sub_sub_subelement_idname] = getid3_lib::BigEndian2Int(substr($EBMLdata, $offset - $EBMLdata_offset, $sub_sub_subelement_length));
                                                                 break;
                                                             case EBML_ID_CHAPTERTRACK:
                                                                 $chaptertrack_entry = array();
                                                                 while ($offset < $sub_sub_subelement_end) {
                                                                     $this->EnsureBufferHasEnoughData($EBMLdata, $offset, $EBMLdata_offset);
                                                                     $sub_sub_sub_subelement_offset = $offset;
                                                                     $sub_sub_sub_subelement_id = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
                                                                     $sub_sub_sub_subelement_idname = $this->EBMLidName($sub_sub_subelement_id);
                                                                     $sub_sub_sub_subelement_length = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
                                                                     $sub_sub_sub_subelement_end = $offset + $sub_sub_sub_subelement_length;
                                                                     switch ($sub_sub_sub_subelement_id) {
                                                                         case EBML_ID_CHAPTERTRACKNUMBER:
                                                                             $chaptertrack_entry[$sub_sub_sub_subelement_idname] = getid3_lib::BigEndian2Int(substr($EBMLdata, $offset - $EBMLdata_offset, $sub_sub_sub_subelement_length));
                                                                             break;
                                                                         default:
                                                                             $this->warnings[] = 'Unhandled chapters.editionentry.chapteratom.chaptertrack element [' . basename(__FILE__) . ':' . __LINE__ . '] (' . $sub_sub_sub_subelement_id . '::' . $sub_sub_sub_subelement_idname . ') at ' . $sub_sub_sub_subelement_offset;
                                                                             break;
                                                                     }
                                                                     $offset = $sub_sub_sub_subelement_end;
                                                                 }
                                                                 $chapteratom_entry[$sub_sub_subelement_idname][] = $chaptertrack_entry;
                                                                 break;
                                                             case EBML_ID_CHAPTERDISPLAY:
                                                                 $chapterdisplay_entry = array();
                                                                 while ($offset < $sub_sub_subelement_end) {
                                                                     $this->EnsureBufferHasEnoughData($EBMLdata, $offset, $EBMLdata_offset);
                                                                     $sub_sub_sub_subelement_offset = $offset;
                                                                     $sub_sub_sub_subelement_id = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
                                                                     $sub_sub_sub_subelement_idname = $this->EBMLidName($sub_sub_sub_subelement_id);
                                                                     $sub_sub_sub_subelement_length = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
                                                                     $sub_sub_sub_subelement_end = $offset + $sub_sub_sub_subelement_length;
                                                                     switch ($sub_sub_sub_subelement_id) {
                                                                         case EBML_ID_CHAPSTRING:
                                                                         case EBML_ID_CHAPLANGUAGE:
                                                                         case EBML_ID_CHAPCOUNTRY:
                                                                             $chapterdisplay_entry[$sub_sub_sub_subelement_idname] = substr($EBMLdata, $offset - $EBMLdata_offset, $sub_sub_sub_subelement_length);
                                                                             break;
                                                                         default:
                                                                             $this->warnings[] = 'Unhandled chapters.editionentry.chapteratom.chapterdisplay element [' . basename(__FILE__) . ':' . __LINE__ . '] (' . $sub_sub_sub_subelement_id . '::' . $sub_sub_sub_subelement_idname . ') at ' . $sub_sub_sub_subelement_offset;
                                                                             break;
                                                                     }
                                                                     $offset = $sub_sub_sub_subelement_end;
                                                                 }
                                                                 $chapteratom_entry[$sub_sub_subelement_idname][] = $chapterdisplay_entry;
                                                                 break;
                                                             default:
                                                                 $this->warnings[] = 'Unhandled chapters.editionentry.chapteratom element [' . basename(__FILE__) . ':' . __LINE__ . '] (' . $sub_sub_subelement_id . '::' . $sub_sub_subelement_idname . ') at ' . $sub_sub_subelement_offset;
                                                                 break;
                                                         }
                                                         $offset = $sub_sub_subelement_end;
                                                     }
                                                     $editionentry_entry[$sub_subelement_idname][] = $chapteratom_entry;
                                                     break;
                                                 default:
                                                     $this->warnings[] = 'Unhandled chapters.editionentry element [' . basename(__FILE__) . ':' . __LINE__ . '] (' . $sub_subelement_id . '::' . $sub_subelement_idname . ') at ' . $sub_subelement_offset;
                                                     break;
                                             }
                                             $offset = $sub_subelement_end;
                                         }
                                         $info['matroska']['chapters'][] = $editionentry_entry;
                                         $offset = $sub_subelement_end;
                                         break;
                                     default:
                                         $this->warnings[] = 'Unhandled chapters element [' . basename(__FILE__) . ':' . __LINE__ . '] (' . $subelement_id . '::' . $subelement_idname . ' [' . $subelement_length . ' bytes]) at ' . $subelement_offset;
                                         break;
                                 }
                                 $offset = $subelement_end;
                             }
                             break;
                         case EBML_ID_VOID:
                             // padding, ignore
                             $void_entry = array();
                             $void_entry['offset'] = $offset;
                             $info['matroska']['void'][] = $void_entry;
                             break;
                         case EBML_ID_CLUSTER:
                             // not important to us, contains mostly actual audio/video data, ignore
                             $cluster_entry = array();
                             while ($offset < $element_end) {
                                 $this->EnsureBufferHasEnoughData($EBMLdata, $offset, $EBMLdata_offset);
                                 $subelement_offset = $offset;
                                 $subelement_id = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
                                 $subelement_idname = $this->EBMLidName($subelement_id);
                                 $subelement_length = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
                                 $subelement_end = $offset + $subelement_length;
                                 switch ($subelement_id) {
                                     case EBML_ID_CLUSTERTIMECODE:
                                     case EBML_ID_CLUSTERPOSITION:
                                     case EBML_ID_CLUSTERPREVSIZE:
                                         $cluster_entry[$subelement_idname] = getid3_lib::BigEndian2Int(substr($EBMLdata, $offset - $EBMLdata_offset, $subelement_length));
                                         break;
                                     case EBML_ID_CLUSTERSILENTTRACKS:
                                         $cluster_silent_tracks = array();
                                         while ($offset < $subelement_end) {
                                             $this->EnsureBufferHasEnoughData($EBMLdata, $offset, $EBMLdata_offset);
                                             $sub_subelement_offset = $offset;
                                             $sub_subelement_id = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
                                             $sub_subelement_idname = $this->EBMLidName($sub_subelement_id);
                                             $sub_subelement_length = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
                                             $sub_subelement_end = $offset + $sub_subelement_length;
                                             switch ($sub_subelement_id) {
                                                 case EBML_ID_CLUSTERSILENTTRACKNUMBER:
                                                     $cluster_silent_tracks[] = getid3_lib::BigEndian2Int(substr($EBMLdata, $offset - $EBMLdata_offset, $sub_subelement_length));
                                                     break;
                                                 default:
                                                     $this->warnings[] = 'Unhandled clusters.silenttracks element [' . basename(__FILE__) . ':' . __LINE__ . '] (' . $sub_subelement_id . '::' . $sub_subelement_idname . ') at ' . $sub_subelement_offset;
                                                     break;
                                             }
                                             $offset = $sub_subelement_end;
                                         }
                                         $cluster_entry[$subelement_idname][] = $cluster_silent_tracks;
                                         $offset = $sub_subelement_end;
                                         break;
                                     case EBML_ID_CLUSTERBLOCKGROUP:
                                         $cluster_block_group = array('offset' => $offset);
                                         while ($offset < $subelement_end) {
                                             $this->EnsureBufferHasEnoughData($EBMLdata, $offset, $EBMLdata_offset);
                                             $sub_subelement_offset = $offset;
                                             $sub_subelement_id = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
                                             $sub_subelement_idname = $this->EBMLidName($sub_subelement_id);
                                             $sub_subelement_length = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
                                             $sub_subelement_end = $offset + $sub_subelement_length;
                                             switch ($sub_subelement_id) {
                                                 case EBML_ID_CLUSTERBLOCK:
                                                     $cluster_block_data = array();
                                                     $cluster_block_data['tracknumber'] = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
                                                     $cluster_block_data['timecode'] = getid3_lib::BigEndian2Int(substr($EBMLdata, $offset, 2));
                                                     $offset += 2;
                                                     // unsure whether this is 1 octect or 2 octets? (http://matroska.org/technical/specs/index.html#block_structure)
                                                     $cluster_block_data['flags_raw'] = getid3_lib::BigEndian2Int(substr($EBMLdata, $offset, 1));
                                                     $offset += 1;
                                                     //$cluster_block_data['flags']['reserved1'] =      (($cluster_block_data['flags_raw'] & 0xF0) >> 4);
                                                     $cluster_block_data['flags']['invisible'] = (bool) (($cluster_block_data['flags_raw'] & 0x8) >> 3);
                                                     $cluster_block_data['flags']['lacing'] = ($cluster_block_data['flags_raw'] & 0x6) >> 1;
                                                     //$cluster_block_data['flags']['reserved2'] =      (($cluster_block_data['flags_raw'] & 0x01) >> 0);
                                                     $cluster_block_data['flags']['lacing_type'] = $this->MatroskaBlockLacingType($cluster_block_data['flags']['lacing']);
                                                     if ($cluster_block_data['flags']['lacing'] != 0) {
                                                         $cluster_block_data['lace_frames'] = getid3_lib::BigEndian2Int(substr($EBMLdata, $offset, 1));
                                                         // Number of frames in the lace-1 (uint8)
                                                         $offset += 1;
                                                         if ($cluster_block_data['flags']['lacing'] != 2) {
                                                             $cluster_block_data['lace_frames'] = getid3_lib::BigEndian2Int(substr($EBMLdata, $offset, 1));
                                                             // Lace-coded size of each frame of the lace, except for the last one (multiple uint8). *This is not used with Fixed-size lacing as it is calculated automatically from (total size of lace) / (number of frames in lace).
                                                             $offset += 1;
                                                         }
                                                     }
                                                     if (!isset($info['matroska']['track_data_offsets'][$cluster_block_data['tracknumber']])) {
                                                         $info['matroska']['track_data_offsets'][$cluster_block_data['tracknumber']]['offset'] = $offset;
                                                         $info['matroska']['track_data_offsets'][$cluster_block_data['tracknumber']]['length'] = $subelement_length;
                                                     }
                                                     $cluster_block_group[$sub_subelement_idname] = $cluster_block_data;
                                                     break;
                                                 case EBML_ID_CLUSTERREFERENCEPRIORITY:
                                                     // unsigned-int
                                                 // unsigned-int
                                                 case EBML_ID_CLUSTERBLOCKDURATION:
                                                     // unsigned-int
                                                     $cluster_block_group[$sub_subelement_idname] = getid3_lib::BigEndian2Int(substr($EBMLdata, $offset - $EBMLdata_offset, $sub_subelement_length));
                                                     break;
                                                 case EBML_ID_CLUSTERREFERENCEBLOCK:
                                                     // signed-int
                                                     $cluster_block_group[$sub_subelement_idname] = getid3_lib::BigEndian2Int(substr($EBMLdata, $offset - $EBMLdata_offset, $sub_subelement_length), false, true);
                                                     break;
                                                 default:
                                                     $this->warnings[] = 'Unhandled clusters.blockgroup element [' . basename(__FILE__) . ':' . __LINE__ . '] (' . $sub_subelement_id . '::' . $sub_subelement_idname . ') at ' . $sub_subelement_offset;
                                                     break;
                                             }
                                             $offset = $sub_subelement_end;
                                         }
                                         $cluster_entry[$subelement_idname][] = $cluster_block_group;
                                         $offset = $sub_subelement_end;
                                         break;
                                     case EBML_ID_CLUSTERSIMPLEBLOCK:
                                         // http://www.matroska.org/technical/specs/index.html#simpleblock_structure
                                         $cluster_block_data = array();
                                         $cluster_block_data['tracknumber'] = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
                                         $cluster_block_data['timecode'] = getid3_lib::BigEndian2Int(substr($EBMLdata, $offset, 2));
                                         $offset += 2;
                                         $cluster_block_data['flags_raw'] = getid3_lib::BigEndian2Int(substr($EBMLdata, $offset, 1));
                                         $offset += 1;
                                         $cluster_block_data['flags']['keyframe'] = ($cluster_block_data['flags_raw'] & 0x80) >> 7;
                                         $cluster_block_data['flags']['reserved1'] = ($cluster_block_data['flags_raw'] & 0x70) >> 4;
                                         $cluster_block_data['flags']['invisible'] = ($cluster_block_data['flags_raw'] & 0x8) >> 3;
                                         $cluster_block_data['flags']['lacing'] = ($cluster_block_data['flags_raw'] & 0x6) >> 1;
                                         // 00=no lacing; 01=Xiph lacing; 11=EBML lacing; 10=fixed-size lacing
                                         $cluster_block_data['flags']['discardable'] = $cluster_block_data['flags_raw'] & 0x1;
                                         if ($cluster_block_data['flags']['lacing'] > 0) {
                                             $cluster_block_data['lace_frames'] = 1 + getid3_lib::BigEndian2Int(substr($EBMLdata, $offset, 1));
                                             $offset += 1;
                                             if ($cluster_block_data['flags']['lacing'] != 0x2) {
                                                 // *This is not used with Fixed-size lacing as it is calculated automatically from (total size of lace) / (number of frames in lace).
                                                 $cluster_block_data['lace_frame_size'] = getid3_lib::BigEndian2Int(substr($EBMLdata, $offset, 1));
                                                 $offset += 1;
                                             }
                                         }
                                         if (!isset($info['matroska']['track_data_offsets'][$cluster_block_data['tracknumber']])) {
                                             $info['matroska']['track_data_offsets'][$cluster_block_data['tracknumber']]['offset'] = $offset;
                                             $info['matroska']['track_data_offsets'][$cluster_block_data['tracknumber']]['length'] = $subelement_length;
                                         }
                                         $cluster_block_group[$sub_subelement_idname] = $cluster_block_data;
                                         break;
                                     default:
                                         $this->warnings[] = 'Unhandled cluster element [' . basename(__FILE__) . ':' . __LINE__ . '] (' . $subelement_id . '::' . $subelement_idname . ' [' . $subelement_length . ' bytes]) at ' . $subelement_offset;
                                         break;
                                 }
                                 $offset = $subelement_end;
                             }
                             $info['matroska']['cluster'][] = $cluster_entry;
                             // check to see if all the data we need exists already, if so, break out of the loop
                             if (isset($info['matroska']['info']) && is_array($info['matroska']['info'])) {
                                 if (isset($info['matroska']['tracks']['tracks']) && is_array($info['matroska']['tracks']['tracks'])) {
                                     break 2;
                                 }
                             }
                             break;
                         default:
                             if ($element_data['id_name'] == dechex($element_data['id'])) {
                                 $info['error'][] = 'Unhandled segment [' . basename(__FILE__) . ':' . __LINE__ . '] (' . $element_data['id'] . ') at ' . $element_data_offset;
                             } else {
                                 $this->warnings[] = 'Unhandled segment [' . basename(__FILE__) . ':' . __LINE__ . '] (' . $element_data['id'] . '::' . $element_data['id_name'] . ') at ' . $element_data['offset'];
                             }
                             break;
                     }
                     $offset = $element_end;
                 }
                 break;
             default:
                 $info['error'][] = 'Unhandled chunk [' . basename(__FILE__) . ':' . __LINE__ . '] (' . $top_element_id . ') at ' . $offset;
                 break;
         }
         $offset = $top_element_endoffset;
     }
     if (isset($info['matroska']['info']) && is_array($info['matroska']['info'])) {
         foreach ($info['matroska']['info'] as $key => $infoarray) {
             if (isset($infoarray['Duration'])) {
                 // TimecodeScale is how many nanoseconds each Duration unit is
                 $info['playtime_seconds'] = $infoarray['Duration'] * ((isset($infoarray['TimecodeScale']) ? $infoarray['TimecodeScale'] : 1000000) / 1000000000);
                 break;
             }
         }
     }
     if (isset($info['matroska']['tags']) && is_array($info['matroska']['tags'])) {
         foreach ($info['matroska']['tags'] as $key => $infoarray) {
             $this->ExtractCommentsSimpleTag($infoarray);
         }
     }
     if (isset($info['matroska']['tracks']['tracks']) && is_array($info['matroska']['tracks']['tracks'])) {
         foreach ($info['matroska']['tracks']['tracks'] as $key => $trackarray) {
             $track_info = array();
             if (isset($trackarray['FlagDefault'])) {
                 $track_info['default'] = $trackarray['FlagDefault'];
             }
             switch (isset($trackarray['TrackType']) ? $trackarray['TrackType'] : '') {
                 case 1:
                     // Video
                     if (!empty($trackarray['PixelWidth'])) {
                         $track_info['resolution_x'] = $trackarray['PixelWidth'];
                     }
                     if (!empty($trackarray['PixelHeight'])) {
                         $track_info['resolution_y'] = $trackarray['PixelHeight'];
                     }
                     if (!empty($trackarray['DisplayWidth'])) {
                         $track_info['display_x'] = $trackarray['DisplayWidth'];
                     }
                     if (!empty($trackarray['DisplayHeight'])) {
                         $track_info['display_y'] = $trackarray['DisplayHeight'];
                     }
                     if (!empty($trackarray['DefaultDuration'])) {
                         $track_info['frame_rate'] = round(1000000000 / $trackarray['DefaultDuration'], 3);
                     }
                     if (!empty($trackarray['CodecID'])) {
                         $track_info['dataformat'] = $this->MatroskaCodecIDtoCommonName($trackarray['CodecID']);
                     }
                     if (!empty($trackarray['codec_private_parsed']['fourcc'])) {
                         $track_info['fourcc'] = $trackarray['codec_private_parsed']['fourcc'];
                     }
                     $info['video']['streams'][] = $track_info;
                     if (isset($track_info['resolution_x']) && empty($info['video']['resolution_x'])) {
                         foreach ($track_info as $key => $value) {
                             $info['video'][$key] = $value;
                         }
                     }
                     break;
                 case 2:
                     // Audio
                     if (!empty($trackarray['CodecID'])) {
                         $track_info['dataformat'] = $this->MatroskaCodecIDtoCommonName($trackarray['CodecID']);
                     }
                     if (!empty($trackarray['SamplingFrequency'])) {
                         $track_info['sample_rate'] = $trackarray['SamplingFrequency'];
                     }
                     if (!empty($trackarray['Channels'])) {
                         $track_info['channels'] = $trackarray['Channels'];
                     }
                     if (!empty($trackarray['BitDepth'])) {
                         $track_info['bits_per_sample'] = $trackarray['BitDepth'];
                     }
                     if (!empty($trackarray['Language'])) {
                         $track_info['language'] = $trackarray['Language'];
                     }
                     switch (isset($trackarray[$this->EBMLidName(EBML_ID_CODECID)]) ? $trackarray[$this->EBMLidName(EBML_ID_CODECID)] : '') {
                         case 'A_PCM/INT/LIT':
                         case 'A_PCM/INT/BIG':
                             $track_info['bitrate'] = $trackarray['SamplingFrequency'] * $trackarray['Channels'] * $trackarray['BitDepth'];
                             break;
                         case 'A_AC3':
                             if (getid3_lib::IncludeDependency(GETID3_INCLUDEPATH . 'module.audio.ac3.php', __FILE__, false)) {
                                 if (isset($info['matroska']['track_data_offsets'][$trackarray['TrackNumber']]['offset'])) {
                                     $getid3_temp = new getID3();
                                     $getid3_temp->openfile($this->getid3->filename);
                                     $getid3_temp->info['avdataoffset'] = $info['matroska']['track_data_offsets'][$trackarray['TrackNumber']]['offset'];
                                     $getid3_ac3 = new getid3_ac3($getid3_temp);
                                     $getid3_ac3->Analyze();
                                     unset($getid3_temp->info['ac3']['GETID3_VERSION']);
                                     $info['matroska']['track_codec_parsed'][$trackarray['TrackNumber']] = $getid3_temp->info['ac3'];
                                     if (!empty($getid3_temp->info['error'])) {
                                         foreach ($getid3_temp->info['error'] as $newerror) {
                                             $this->warnings[] = 'getid3_ac3() says: [' . $newerror . ']';
                                         }
                                     }
                                     if (!empty($getid3_temp->info['warning'])) {
                                         foreach ($getid3_temp->info['warning'] as $newerror) {
                                             $this->warnings[] = 'getid3_ac3() says: [' . $newerror . ']';
                                         }
                                     }
                                     if (isset($getid3_temp->info['audio']) && is_array($getid3_temp->info['audio'])) {
                                         foreach ($getid3_temp->info['audio'] as $key => $value) {
                                             $track_info[$key] = $value;
                                         }
                                     }
                                     unset($getid3_temp, $getid3_ac3);
                                 } else {
                                     $this->warnings[] = 'Unable to parse audio data [' . basename(__FILE__) . ':' . __LINE__ . '] because $info[matroska][track_data_offsets][' . $trackarray['TrackNumber'] . '][offset] not set';
                                 }
                             } else {
                                 $this->warnings[] = 'Unable to parse audio data [' . basename(__FILE__) . ':' . __LINE__ . '] because cannot include "module.audio.ac3.php"';
                             }
                             break;
                         case 'A_DTS':
                             $dts_offset = $info['matroska']['track_data_offsets'][$trackarray['TrackNumber']]['offset'];
                             // this is a NASTY hack, but sometimes audio data is off by a byte or two and not sure why, email info@getid3.org if you can explain better
                             fseek($this->getid3->fp, $dts_offset, SEEK_SET);
                             $magic_test = fread($this->getid3->fp, 8);
                             for ($i = 0; $i < 4; $i++) {
                                 // look to see if DTS "magic" is here, if so adjust offset by that many bytes
                                 if (substr($magic_test, $i, 4) == "þ€") {
                                     $dts_offset += $i;
                                     break;
                                 }
                             }
                             if (getid3_lib::IncludeDependency(GETID3_INCLUDEPATH . 'module.audio.dts.php', __FILE__, false)) {
                                 $getid3_temp = new getID3();
                                 $getid3_temp->openfile($this->getid3->filename);
                                 $getid3_temp->info['avdataoffset'] = $dts_offset;
                                 $getid3_dts = new getid3_dts($getid3_temp);
                                 $getid3_dts->Analyze();
                                 unset($getid3_temp->info['dts']['GETID3_VERSION']);
                                 $info['matroska']['track_codec_parsed'][$trackarray['TrackNumber']] = $getid3_temp->info['dts'];
                                 if (!empty($getid3_temp->info['error'])) {
                                     foreach ($getid3_temp->info['error'] as $newerror) {
                                         $this->warnings[] = 'getid3_dts() says: [' . $newerror . ']';
                                     }
                                 }
                                 if (!empty($getid3_temp->info['warning'])) {
                                     foreach ($getid3_temp->info['warning'] as $newerror) {
                                         $this->warnings[] = 'getid3_dts() says: [' . $newerror . ']';
                                     }
                                 }
                                 if (isset($getid3_temp->info['audio']) && is_array($getid3_temp->info['audio'])) {
                                     foreach ($getid3_temp->info['audio'] as $key => $value) {
                                         $track_info[$key] = $value;
                                     }
                                 }
                                 unset($getid3_temp, $getid3_dts);
                             } else {
                                 $this->warnings[] = 'Unable to parse audio data [' . basename(__FILE__) . ':' . __LINE__ . '] because cannot include "module.audio.dts.php"';
                             }
                             break;
                         case 'A_AAC':
                             $this->warnings[] = 'This version of getID3() [v' . $this->getid3->version() . '] has problems parsing AAC audio in Matroska containers [' . basename(__FILE__) . ':' . __LINE__ . ']';
                             if (getid3_lib::IncludeDependency(GETID3_INCLUDEPATH . 'module.audio.aac.php', __FILE__, false)) {
                                 $getid3_temp = new getID3();
                                 $getid3_temp->openfile($this->getid3->filename);
                                 $getid3_temp->info['avdataoffset'] = $info['matroska']['track_data_offsets'][$trackarray['TrackNumber']]['offset'];
                                 $getid3_aac = new getid3_aac($getid3_temp);
                                 $getid3_aac->Analyze();
                                 unset($getid3_temp->info['aac']['GETID3_VERSION']);
                                 if (!empty($getid3_temp->info['audio']['dataformat'])) {
                                     $info['matroska']['track_codec_parsed'][$trackarray['TrackNumber']] = $getid3_temp->info['aac'];
                                     if (isset($getid3_temp->info['audio']) && is_array($getid3_temp->info['audio'])) {
                                         foreach ($getid3_temp->info['audio'] as $key => $value) {
                                             $track_info[$key] = $value;
                                         }
                                     }
                                 } else {
                                     $this->warnings[] = 'Failed to parse ' . $trackarray[$this->EBMLidName(EBML_ID_CODECID)] . ' audio data [' . basename(__FILE__) . ':' . __LINE__ . ']';
                                 }
                                 unset($getid3_temp, $getid3_aac);
                             } else {
                                 $this->warnings[] = 'Unable to parse audio data [' . basename(__FILE__) . ':' . __LINE__ . '] because cannot include "module.audio.aac.php"';
                             }
                             break;
                         case 'A_MPEG/L3':
                             if (getid3_lib::IncludeDependency(GETID3_INCLUDEPATH . 'module.audio.mp3.php', __FILE__, false)) {
                                 $getid3_temp = new getID3();
                                 $getid3_temp->openfile($this->getid3->filename);
                                 $getid3_temp->info['avdataoffset'] = $info['matroska']['track_data_offsets'][$trackarray['TrackNumber']]['offset'];
                                 $getid3_temp->info['avdataend'] = $info['matroska']['track_data_offsets'][$trackarray['TrackNumber']]['offset'] + $info['matroska']['track_data_offsets'][$trackarray['TrackNumber']]['length'];
                                 $getid3_mp3 = new getid3_mp3($getid3_temp);
                                 $getid3_mp3->allow_bruteforce = true;
                                 $getid3_mp3->Analyze();
                                 if (!empty($getid3_temp->info['mpeg'])) {
                                     unset($getid3_temp->info['mpeg']['GETID3_VERSION']);
                                     $info['matroska']['track_codec_parsed'][$trackarray['TrackNumber']] = $getid3_temp->info['mpeg'];
                                     if (!empty($getid3_temp->info['error'])) {
                                         foreach ($getid3_temp->info['error'] as $newerror) {
                                             $this->warnings[] = 'getid3_mp3() says: [' . $newerror . ']';
                                         }
                                     }
                                     if (!empty($getid3_temp->info['warning'])) {
                                         foreach ($getid3_temp->info['warning'] as $newerror) {
                                             $this->warnings[] = 'getid3_mp3() says: [' . $newerror . ']';
                                         }
                                     }
                                     if (isset($getid3_temp->info['audio']) && is_array($getid3_temp->info['audio'])) {
                                         foreach ($getid3_temp->info['audio'] as $key => $value) {
                                             $track_info[$key] = $value;
                                         }
                                     }
                                 } else {
                                     $this->warnings[] = 'Unable to parse audio data [' . basename(__FILE__) . ':' . __LINE__ . '] because getid3_mp3::Analyze failed at offset ' . $info['matroska']['track_data_offsets'][$trackarray['TrackNumber']]['offset'];
                                 }
                                 unset($getid3_temp, $getid3_mp3);
                             } else {
                                 $this->warnings[] = 'Unable to parse audio data [' . basename(__FILE__) . ':' . __LINE__ . '] because cannot include "module.audio.mp3.php"';
                             }
                             break;
                         case 'A_VORBIS':
                             if (isset($trackarray['CodecPrivate'])) {
                                 // this is a NASTY hack, email info@getid3.org if you have a better idea how to get this info out
                                 $found_vorbis = false;
                                 for ($vorbis_offset = 1; $vorbis_offset < 16; $vorbis_offset++) {
                                     if (substr($trackarray['CodecPrivate'], $vorbis_offset, 6) == 'vorbis') {
                                         $vorbis_offset--;
                                         $found_vorbis = true;
                                         break;
                                     }
                                 }
                                 if ($found_vorbis) {
                                     if (getid3_lib::IncludeDependency(GETID3_INCLUDEPATH . 'module.audio.ogg.php', __FILE__, false)) {
                                         $oggpageinfo['page_seqno'] = 0;
                                         $getid3_temp = new getID3();
                                         $getid3_temp->openfile($this->getid3->filename);
                                         $getid3_ogg = new getid3_ogg($getid3_temp);
                                         $getid3_ogg->ParseVorbisPageHeader($trackarray['CodecPrivate'], $vorbis_offset, $oggpageinfo);
                                         $vorbis_fileinfo = $getid3_temp->info;
                                         unset($getid3_temp, $getid3_ogg);
                                         if (isset($vorbis_fileinfo['audio'])) {
                                             $info['matroska']['track_codec_parsed'][$trackarray['TrackNumber']]['audio'] = $vorbis_fileinfo['audio'];
                                         }
                                         if (isset($vorbis_fileinfo['ogg'])) {
                                             $info['matroska']['track_codec_parsed'][$trackarray['TrackNumber']]['ogg'] = $vorbis_fileinfo['ogg'];
                                         }
                                         if (!empty($vorbis_fileinfo['error'])) {
                                             foreach ($vorbis_fileinfo['error'] as $newerror) {
                                                 $this->warnings[] = 'getid3_ogg() says: [' . $newerror . ']';
                                             }
                                         }
                                         if (!empty($vorbis_fileinfo['warning'])) {
                                             foreach ($vorbis_fileinfo['warning'] as $newerror) {
                                                 $this->warnings[] = 'getid3_ogg() says: [' . $newerror . ']';
                                             }
                                         }
                                         if (isset($vorbis_fileinfo['audio']) && is_array($vorbis_fileinfo['audio'])) {
                                             foreach ($vorbis_fileinfo['audio'] as $key => $value) {
                                                 $track_info[$key] = $value;
                                             }
                                         }
                                         if (!empty($vorbis_fileinfo['ogg']['bitrate_average'])) {
                                             $track_info['bitrate'] = $vorbis_fileinfo['ogg']['bitrate_average'];
                                         } elseif (!empty($vorbis_fileinfo['ogg']['bitrate_nominal'])) {
                                             $track_info['bitrate'] = $vorbis_fileinfo['ogg']['bitrate_nominal'];
                                         }
                                         unset($vorbis_fileinfo);
                                         unset($oggpageinfo);
                                     } else {
                                         $this->warnings[] = 'Unable to parse audio data [' . basename(__FILE__) . ':' . __LINE__ . '] because cannot include "module.audio.ogg.php"';
                                     }
                                 } else {
                                 }
                             } else {
                             }
                             break;
                         default:
                             $this->warnings[] = 'Unhandled audio type "' . (isset($trackarray[$this->EBMLidName(EBML_ID_CODECID)]) ? $trackarray[$this->EBMLidName(EBML_ID_CODECID)] : '') . '"';
                             break;
                     }
                     $info['audio']['streams'][] = $track_info;
                     if (isset($track_info['dataformat']) && empty($info['audio']['dataformat'])) {
                         foreach ($track_info as $key => $value) {
                             $info['audio'][$key] = $value;
                         }
                     }
                     break;
                 default:
                     // ignore, do nothing
                     break;
             }
         }
     }
     if ($this->hide_clusters) {
         // too much data returned that is usually not useful
         if (isset($info['matroska']['segments']) && is_array($info['matroska']['segments'])) {
             foreach ($info['matroska']['segments'] as $key => $segmentsarray) {
                 if ($segmentsarray['id'] == EBML_ID_CLUSTER) {
                     unset($info['matroska']['segments'][$key]);
                 }
             }
         }
         if (isset($info['matroska']['seek']) && is_array($info['matroska']['seek'])) {
             foreach ($info['matroska']['seek'] as $key => $seekarray) {
                 if ($seekarray['target_id'] == EBML_ID_CLUSTER) {
                     unset($info['matroska']['seek'][$key]);
                 }
             }
         }
         //unset($info['matroska']['cluster']);
         //unset($info['matroska']['track_data_offsets']);
     }
     if (!empty($info['video']['streams'])) {
         $info['mime_type'] = 'video/x-matroska';
     } elseif (!empty($info['audio']['streams'])) {
         $info['mime_type'] = 'audio/x-matroska';
     } elseif (isset($info['mime_type'])) {
         unset($info['mime_type']);
     }
     foreach ($this->warnings as $key => $value) {
         $info['warning'][] = $value;
     }
     return true;
 }
 public function ParseRIFF($startoffset, $maxoffset)
 {
     $info =& $this->getid3->info;
     $RIFFchunk = false;
     $FoundAllChunksWeNeed = false;
     try {
         $this->fseek($startoffset);
         $maxoffset = min($maxoffset, $info['avdataend']);
         while ($this->ftell() < $maxoffset) {
             $chunknamesize = $this->fread(8);
             //$chunkname =                          substr($chunknamesize, 0, 4);
             $chunkname = str_replace("", '_', substr($chunknamesize, 0, 4));
             // note: chunk names of 4 null bytes do appear to be legal (has been observed inside INFO and PRMI chunks, for example), but makes traversing array keys more difficult
             $chunksize = $this->EitherEndian2Int(substr($chunknamesize, 4, 4));
             //if (strlen(trim($chunkname, "\x00")) < 4) {
             if (strlen($chunkname) < 4) {
                 $this->error('Expecting chunk name at offset ' . ($this->ftell() - 8) . ' but found nothing. Aborting RIFF parsing.');
                 break;
             }
             if ($chunksize == 0 && $chunkname != 'JUNK') {
                 $this->warning('Chunk (' . $chunkname . ') size at offset ' . ($this->ftell() - 4) . ' is zero. Aborting RIFF parsing.');
                 break;
             }
             if ($chunksize % 2 != 0) {
                 // all structures are packed on word boundaries
                 $chunksize++;
             }
             switch ($chunkname) {
                 case 'LIST':
                     $listname = $this->fread(4);
                     if (preg_match('#^(movi|rec )$#i', $listname)) {
                         $RIFFchunk[$listname]['offset'] = $this->ftell() - 4;
                         $RIFFchunk[$listname]['size'] = $chunksize;
                         if (!$FoundAllChunksWeNeed) {
                             $WhereWeWere = $this->ftell();
                             $AudioChunkHeader = $this->fread(12);
                             $AudioChunkStreamNum = substr($AudioChunkHeader, 0, 2);
                             $AudioChunkStreamType = substr($AudioChunkHeader, 2, 2);
                             $AudioChunkSize = getid3_lib::LittleEndian2Int(substr($AudioChunkHeader, 4, 4));
                             if ($AudioChunkStreamType == 'wb') {
                                 $FirstFourBytes = substr($AudioChunkHeader, 8, 4);
                                 if (preg_match('/^\\xFF[\\xE2-\\xE7\\xF2-\\xF7\\xFA-\\xFF][\\x00-\\xEB]/s', $FirstFourBytes)) {
                                     // MP3
                                     if (getid3_mp3::MPEGaudioHeaderBytesValid($FirstFourBytes)) {
                                         $getid3_temp = new getID3();
                                         $getid3_temp->openfile($this->getid3->filename);
                                         $getid3_temp->info['avdataoffset'] = $this->ftell() - 4;
                                         $getid3_temp->info['avdataend'] = $this->ftell() + $AudioChunkSize;
                                         $getid3_mp3 = new getid3_mp3($getid3_temp, __CLASS__);
                                         $getid3_mp3->getOnlyMPEGaudioInfo($getid3_temp->info['avdataoffset'], false);
                                         if (isset($getid3_temp->info['mpeg']['audio'])) {
                                             $info['mpeg']['audio'] = $getid3_temp->info['mpeg']['audio'];
                                             $info['audio'] = $getid3_temp->info['audio'];
                                             $info['audio']['dataformat'] = 'mp' . $info['mpeg']['audio']['layer'];
                                             $info['audio']['sample_rate'] = $info['mpeg']['audio']['sample_rate'];
                                             $info['audio']['channels'] = $info['mpeg']['audio']['channels'];
                                             $info['audio']['bitrate'] = $info['mpeg']['audio']['bitrate'];
                                             $info['audio']['bitrate_mode'] = strtolower($info['mpeg']['audio']['bitrate_mode']);
                                             //$info['bitrate']               = $info['audio']['bitrate'];
                                         }
                                         unset($getid3_temp, $getid3_mp3);
                                     }
                                 } elseif (strpos($FirstFourBytes, getid3_ac3::syncword) === 0) {
                                     // AC3
                                     $getid3_temp = new getID3();
                                     $getid3_temp->openfile($this->getid3->filename);
                                     $getid3_temp->info['avdataoffset'] = $this->ftell() - 4;
                                     $getid3_temp->info['avdataend'] = $this->ftell() + $AudioChunkSize;
                                     $getid3_ac3 = new getid3_ac3($getid3_temp);
                                     $getid3_ac3->Analyze();
                                     if (empty($getid3_temp->info['error'])) {
                                         $info['audio'] = $getid3_temp->info['audio'];
                                         $info['ac3'] = $getid3_temp->info['ac3'];
                                         if (!empty($getid3_temp->info['warning'])) {
                                             foreach ($getid3_temp->info['warning'] as $key => $value) {
                                                 $info['warning'][] = $value;
                                             }
                                         }
                                     }
                                     unset($getid3_temp, $getid3_ac3);
                                 }
                             }
                             $FoundAllChunksWeNeed = true;
                             $this->fseek($WhereWeWere);
                         }
                         $this->fseek($chunksize - 4, SEEK_CUR);
                     } else {
                         if (!isset($RIFFchunk[$listname])) {
                             $RIFFchunk[$listname] = array();
                         }
                         $LISTchunkParent = $listname;
                         $LISTchunkMaxOffset = $this->ftell() - 4 + $chunksize;
                         if ($parsedChunk = $this->ParseRIFF($this->ftell(), $LISTchunkMaxOffset)) {
                             $RIFFchunk[$listname] = array_merge_recursive($RIFFchunk[$listname], $parsedChunk);
                         }
                     }
                     break;
                 default:
                     if (preg_match('#^[0-9]{2}(wb|pc|dc|db)$#', $chunkname)) {
                         $this->fseek($chunksize, SEEK_CUR);
                         break;
                     }
                     $thisindex = 0;
                     if (isset($RIFFchunk[$chunkname]) && is_array($RIFFchunk[$chunkname])) {
                         $thisindex = count($RIFFchunk[$chunkname]);
                     }
                     $RIFFchunk[$chunkname][$thisindex]['offset'] = $this->ftell() - 8;
                     $RIFFchunk[$chunkname][$thisindex]['size'] = $chunksize;
                     switch ($chunkname) {
                         case 'data':
                             $info['avdataoffset'] = $this->ftell();
                             $info['avdataend'] = $info['avdataoffset'] + $chunksize;
                             $testData = $this->fread(36);
                             if ($testData === '') {
                                 break;
                             }
                             if (preg_match('/^\\xFF[\\xE2-\\xE7\\xF2-\\xF7\\xFA-\\xFF][\\x00-\\xEB]/s', substr($testData, 0, 4))) {
                                 // Probably is MP3 data
                                 if (getid3_mp3::MPEGaudioHeaderBytesValid(substr($testData, 0, 4))) {
                                     $getid3_temp = new getID3();
                                     $getid3_temp->openfile($this->getid3->filename);
                                     $getid3_temp->info['avdataoffset'] = $info['avdataoffset'];
                                     $getid3_temp->info['avdataend'] = $info['avdataend'];
                                     $getid3_mp3 = new getid3_mp3($getid3_temp, __CLASS__);
                                     $getid3_mp3->getOnlyMPEGaudioInfo($info['avdataoffset'], false);
                                     if (empty($getid3_temp->info['error'])) {
                                         $info['audio'] = $getid3_temp->info['audio'];
                                         $info['mpeg'] = $getid3_temp->info['mpeg'];
                                     }
                                     unset($getid3_temp, $getid3_mp3);
                                 }
                             } elseif (($isRegularAC3 = substr($testData, 0, 2) == getid3_ac3::syncword) || substr($testData, 8, 2) == strrev(getid3_ac3::syncword)) {
                                 // This is probably AC-3 data
                                 $getid3_temp = new getID3();
                                 if ($isRegularAC3) {
                                     $getid3_temp->openfile($this->getid3->filename);
                                     $getid3_temp->info['avdataoffset'] = $info['avdataoffset'];
                                     $getid3_temp->info['avdataend'] = $info['avdataend'];
                                 }
                                 $getid3_ac3 = new getid3_ac3($getid3_temp);
                                 if ($isRegularAC3) {
                                     $getid3_ac3->Analyze();
                                 } else {
                                     // Dolby Digital WAV
                                     // AC-3 content, but not encoded in same format as normal AC-3 file
                                     // For one thing, byte order is swapped
                                     $ac3_data = '';
                                     for ($i = 0; $i < 28; $i += 2) {
                                         $ac3_data .= substr($testData, 8 + $i + 1, 1);
                                         $ac3_data .= substr($testData, 8 + $i + 0, 1);
                                     }
                                     $getid3_ac3->AnalyzeString($ac3_data);
                                 }
                                 if (empty($getid3_temp->info['error'])) {
                                     $info['audio'] = $getid3_temp->info['audio'];
                                     $info['ac3'] = $getid3_temp->info['ac3'];
                                     if (!empty($getid3_temp->info['warning'])) {
                                         foreach ($getid3_temp->info['warning'] as $newerror) {
                                             $this->warning('getid3_ac3() says: [' . $newerror . ']');
                                         }
                                     }
                                 }
                                 unset($getid3_temp, $getid3_ac3);
                             } elseif (preg_match('/^(' . implode('|', array_map('preg_quote', getid3_dts::$syncwords)) . ')/', $testData)) {
                                 // This is probably DTS data
                                 $getid3_temp = new getID3();
                                 $getid3_temp->openfile($this->getid3->filename);
                                 $getid3_temp->info['avdataoffset'] = $info['avdataoffset'];
                                 $getid3_dts = new getid3_dts($getid3_temp);
                                 $getid3_dts->Analyze();
                                 if (empty($getid3_temp->info['error'])) {
                                     $info['audio'] = $getid3_temp->info['audio'];
                                     $info['dts'] = $getid3_temp->info['dts'];
                                     $info['playtime_seconds'] = $getid3_temp->info['playtime_seconds'];
                                     // may not match RIFF calculations since DTS-WAV often used 14/16 bit-word packing
                                     if (!empty($getid3_temp->info['warning'])) {
                                         foreach ($getid3_temp->info['warning'] as $newerror) {
                                             $this->warning('getid3_dts() says: [' . $newerror . ']');
                                         }
                                     }
                                 }
                                 unset($getid3_temp, $getid3_dts);
                             } elseif (substr($testData, 0, 4) == 'wvpk') {
                                 // This is WavPack data
                                 $info['wavpack']['offset'] = $info['avdataoffset'];
                                 $info['wavpack']['size'] = getid3_lib::LittleEndian2Int(substr($testData, 4, 4));
                                 $this->parseWavPackHeader(substr($testData, 8, 28));
                             } else {
                                 // This is some other kind of data (quite possibly just PCM)
                                 // do nothing special, just skip it
                             }
                             $nextoffset = $info['avdataend'];
                             $this->fseek($nextoffset);
                             break;
                         case 'iXML':
                         case 'bext':
                         case 'cart':
                         case 'fmt ':
                         case 'strh':
                         case 'strf':
                         case 'indx':
                         case 'MEXT':
                         case 'DISP':
                             // always read data in
                         // always read data in
                         case 'JUNK':
                             // should be: never read data in
                             // but some programs write their version strings in a JUNK chunk (e.g. VirtualDub, AVIdemux, etc)
                             if ($chunksize < 1048576) {
                                 if ($chunksize > 0) {
                                     $RIFFchunk[$chunkname][$thisindex]['data'] = $this->fread($chunksize);
                                     if ($chunkname == 'JUNK') {
                                         if (preg_match('#^([\\x20-\\x7F]+)#', $RIFFchunk[$chunkname][$thisindex]['data'], $matches)) {
                                             // only keep text characters [chr(32)-chr(127)]
                                             $info['riff']['comments']['junk'][] = trim($matches[1]);
                                         }
                                         // but if nothing there, ignore
                                         // remove the key in either case
                                         unset($RIFFchunk[$chunkname][$thisindex]['data']);
                                     }
                                 }
                             } else {
                                 $this->warning('Chunk "' . $chunkname . '" at offset ' . $this->ftell() . ' is unexpectedly larger than 1MB (claims to be ' . number_format($chunksize) . ' bytes), skipping data');
                                 $this->fseek($chunksize, SEEK_CUR);
                             }
                             break;
                             //case 'IDVX':
                             //	$info['divxtag']['comments'] = self::ParseDIVXTAG($this->fread($chunksize));
                             //	break;
                         //case 'IDVX':
                         //	$info['divxtag']['comments'] = self::ParseDIVXTAG($this->fread($chunksize));
                         //	break;
                         default:
                             if (!empty($LISTchunkParent) && $RIFFchunk[$chunkname][$thisindex]['offset'] + $RIFFchunk[$chunkname][$thisindex]['size'] <= $LISTchunkMaxOffset) {
                                 $RIFFchunk[$LISTchunkParent][$chunkname][$thisindex]['offset'] = $RIFFchunk[$chunkname][$thisindex]['offset'];
                                 $RIFFchunk[$LISTchunkParent][$chunkname][$thisindex]['size'] = $RIFFchunk[$chunkname][$thisindex]['size'];
                                 unset($RIFFchunk[$chunkname][$thisindex]['offset']);
                                 unset($RIFFchunk[$chunkname][$thisindex]['size']);
                                 if (isset($RIFFchunk[$chunkname][$thisindex]) && empty($RIFFchunk[$chunkname][$thisindex])) {
                                     unset($RIFFchunk[$chunkname][$thisindex]);
                                 }
                                 if (isset($RIFFchunk[$chunkname]) && empty($RIFFchunk[$chunkname])) {
                                     unset($RIFFchunk[$chunkname]);
                                 }
                                 $RIFFchunk[$LISTchunkParent][$chunkname][$thisindex]['data'] = $this->fread($chunksize);
                             } elseif ($chunksize < 2048) {
                                 // only read data in if smaller than 2kB
                                 $RIFFchunk[$chunkname][$thisindex]['data'] = $this->fread($chunksize);
                             } else {
                                 $this->fseek($chunksize, SEEK_CUR);
                             }
                             break;
                     }
                     break;
             }
         }
     } catch (getid3_exception $e) {
         if ($e->getCode() == 10) {
             $this->warning('RIFF parser: ' . $e->getMessage());
         } else {
             throw $e;
         }
     }
     return $RIFFchunk;
 }
 function MPEGaudioHeaderValid($rawarray, $echoerrors = false, $allowBitrate15 = false)
 {
     if (($rawarray['synch'] & 0xffe) != 0xffe) {
         return false;
     }
     static $MPEGaudioVersionLookup;
     static $MPEGaudioLayerLookup;
     static $MPEGaudioBitrateLookup;
     static $MPEGaudioFrequencyLookup;
     static $MPEGaudioChannelModeLookup;
     static $MPEGaudioModeExtensionLookup;
     static $MPEGaudioEmphasisLookup;
     if (empty($MPEGaudioVersionLookup)) {
         $MPEGaudioVersionLookup = getid3_mp3::MPEGaudioVersionArray();
         $MPEGaudioLayerLookup = getid3_mp3::MPEGaudioLayerArray();
         $MPEGaudioBitrateLookup = getid3_mp3::MPEGaudioBitrateArray();
         $MPEGaudioFrequencyLookup = getid3_mp3::MPEGaudioFrequencyArray();
         $MPEGaudioChannelModeLookup = getid3_mp3::MPEGaudioChannelModeArray();
         $MPEGaudioModeExtensionLookup = getid3_mp3::MPEGaudioModeExtensionArray();
         $MPEGaudioEmphasisLookup = getid3_mp3::MPEGaudioEmphasisArray();
     }
     if (isset($MPEGaudioVersionLookup[$rawarray['version']])) {
         $decodedVersion = $MPEGaudioVersionLookup[$rawarray['version']];
     } else {
         echo $echoerrors ? "\n" . 'invalid Version (' . $rawarray['version'] . ')' : '';
         return false;
     }
     if (isset($MPEGaudioLayerLookup[$rawarray['layer']])) {
         $decodedLayer = $MPEGaudioLayerLookup[$rawarray['layer']];
     } else {
         echo $echoerrors ? "\n" . 'invalid Layer (' . $rawarray['layer'] . ')' : '';
         return false;
     }
     if (!isset($MPEGaudioBitrateLookup[$decodedVersion][$decodedLayer][$rawarray['bitrate']])) {
         echo $echoerrors ? "\n" . 'invalid Bitrate (' . $rawarray['bitrate'] . ')' : '';
         if ($rawarray['bitrate'] == 15) {
             // known issue in LAME 3.90 - 3.93.1 where free-format has bitrate ID of 15 instead of 0
             // let it go through here otherwise file will not be identified
             if (!$allowBitrate15) {
                 return false;
             }
         } else {
             return false;
         }
     }
     if (!isset($MPEGaudioFrequencyLookup[$decodedVersion][$rawarray['sample_rate']])) {
         echo $echoerrors ? "\n" . 'invalid Frequency (' . $rawarray['sample_rate'] . ')' : '';
         return false;
     }
     if (!isset($MPEGaudioChannelModeLookup[$rawarray['channelmode']])) {
         echo $echoerrors ? "\n" . 'invalid ChannelMode (' . $rawarray['channelmode'] . ')' : '';
         return false;
     }
     if (!isset($MPEGaudioModeExtensionLookup[$decodedLayer][$rawarray['modeextension']])) {
         echo $echoerrors ? "\n" . 'invalid Mode Extension (' . $rawarray['modeextension'] . ')' : '';
         return false;
     }
     if (!isset($MPEGaudioEmphasisLookup[$rawarray['emphasis']])) {
         echo $echoerrors ? "\n" . 'invalid Emphasis (' . $rawarray['emphasis'] . ')' : '';
         return false;
     }
     // These are just either set or not set, you can't mess that up :)
     // $rawarray['protection'];
     // $rawarray['padding'];
     // $rawarray['private'];
     // $rawarray['copyright'];
     // $rawarray['original'];
     return true;
 }
Exemplo n.º 5
0
                echo '<td><a href="' . htmlentities($_SERVER['PHP_SELF'] . '?encodedbydistribution=' . urlencode($string) . '&showfiles=1') . '">' . $string . '</a></td></tr>';
            }
            echo '</table></td></tr>';
        }
        echo '</table>';
    }
} elseif (!empty($_REQUEST['audiobitrates'])) {
    getid3_lib::IncludeDependency(GETID3_INCLUDEPATH . 'module.audio.mp3.php', __FILE__, true);
    $BitrateDistribution = array();
    $SQLquery = 'SELECT ROUND(audio_bitrate / 1000) AS `RoundBitrate`, COUNT(*) AS `num`';
    $SQLquery .= ' FROM `' . mysql_real_escape_string(GETID3_DB_TABLE) . '`';
    $SQLquery .= ' WHERE (`audio_bitrate` > 0)';
    $SQLquery .= ' GROUP BY `RoundBitrate`';
    $result = mysql_query_safe($SQLquery);
    while ($row = mysql_fetch_array($result)) {
        $this_bitrate = getid3_mp3::ClosestStandardMP3Bitrate($row['RoundBitrate'] * 1000);
        if (isset($BitrateDistribution[$this_bitrate])) {
            $BitrateDistribution[$this_bitrate] += $row['num'];
        } else {
            $BitrateDistribution[$this_bitrate] = $row['num'];
        }
    }
    echo '<table border="1" cellspacing="0" cellpadding="3">';
    echo '<tr><th>Bitrate</th><th>Count</th></tr>';
    foreach ($BitrateDistribution as $Bitrate => $Count) {
        echo '<tr>';
        echo '<td align="right">' . round($Bitrate / 1000) . ' kbps</td>';
        echo '<td align="right">' . number_format($Count) . '</td>';
        echo '</tr>';
    }
    echo '</table>';
Exemplo n.º 6
0
                echo '<td><a href="' . $_SERVER['SCRIPT_NAME'] . '?encodedbydistribution=' . urlencode($string) . '&showfiles=1">' . $string . '</a></td></tr>';
            }
            echo '</table></td></tr>';
        }
        echo '</table>';
    }
} elseif (!empty($_REQUEST['audiobitrates'])) {
    getid3_lib::IncludeDependency(GETID3_INCLUDEPATH . 'module.audio.mp3.php', __FILE__, true);
    $BitrateDistribution = array();
    $SQLquery = 'SELECT ROUND(audio_bitrate / 1000) AS `RoundBitrate`, COUNT(*) AS `num`';
    $SQLquery .= ' FROM `' . GETID3_DB_TABLE . '`';
    $SQLquery .= ' WHERE (`audio_bitrate` > 0)';
    $SQLquery .= ' GROUP BY `RoundBitrate`';
    $result = safe_mysql_query($SQLquery);
    while ($row = mysql_fetch_array($result)) {
        @($BitrateDistribution[getid3_mp3::ClosestStandardMP3Bitrate($row['RoundBitrate'] * 1000)] += $row['num']);
        // safe_inc
    }
    echo '<table border="1" cellspacing="0" cellpadding="3">';
    echo '<tr><th>Bitrate</th><th>Count</th></tr>';
    foreach ($BitrateDistribution as $Bitrate => $Count) {
        echo '<tr>';
        echo '<TD ALIGN="RIGHT">' . round($Bitrate / 1000) . ' kbps</td>';
        echo '<TD ALIGN="RIGHT">' . number_format($Count) . '</td>';
        echo '</tr>';
    }
    echo '</table>';
} elseif (!empty($_REQUEST['emptygenres'])) {
    $SQLquery = 'SELECT `fileformat`, `filename`, `genre`';
    $SQLquery .= ' FROM `' . GETID3_DB_TABLE . '`';
    $SQLquery .= ' WHERE (`genre` = "")';
 public function QuicktimeParseAtom($atomname, $atomsize, $atom_data, $baseoffset, &$atomHierarchy, $ParseAllPossibleAtoms)
 {
     // http://developer.apple.com/techpubs/quicktime/qtdevdocs/APIREF/INDEX/atomalphaindex.htm
     $info =& $this->getid3->info;
     $atom_parent = end($atomHierarchy);
     // not array_pop($atomHierarchy); see http://www.getid3.org/phpBB3/viewtopic.php?t=1717
     array_push($atomHierarchy, $atomname);
     $atom_structure['hierarchy'] = implode(' ', $atomHierarchy);
     $atom_structure['name'] = $atomname;
     $atom_structure['size'] = $atomsize;
     $atom_structure['offset'] = $baseoffset;
     switch ($atomname) {
         case 'moov':
             // MOVie container atom
         // MOVie container atom
         case 'trak':
             // TRAcK container atom
         // TRAcK container atom
         case 'clip':
             // CLIPping container atom
         // CLIPping container atom
         case 'matt':
             // track MATTe container atom
         // track MATTe container atom
         case 'edts':
             // EDiTS container atom
         // EDiTS container atom
         case 'tref':
             // Track REFerence container atom
         // Track REFerence container atom
         case 'mdia':
             // MeDIA container atom
         // MeDIA container atom
         case 'minf':
             // Media INFormation container atom
         // Media INFormation container atom
         case 'dinf':
             // Data INFormation container atom
         // Data INFormation container atom
         case 'udta':
             // User DaTA container atom
         // User DaTA container atom
         case 'cmov':
             // Compressed MOVie container atom
         // Compressed MOVie container atom
         case 'rmra':
             // Reference Movie Record Atom
         // Reference Movie Record Atom
         case 'rmda':
             // Reference Movie Descriptor Atom
         // Reference Movie Descriptor Atom
         case 'gmhd':
             // Generic Media info HeaDer atom (seen on QTVR)
             $atom_structure['subatoms'] = $this->QuicktimeParseContainerAtom($atom_data, $baseoffset + 8, $atomHierarchy, $ParseAllPossibleAtoms);
             break;
         case 'ilst':
             // Item LiST container atom
             if ($atom_structure['subatoms'] = $this->QuicktimeParseContainerAtom($atom_data, $baseoffset + 8, $atomHierarchy, $ParseAllPossibleAtoms)) {
                 // some "ilst" atoms contain data atoms that have a numeric name, and the data is far more accessible if the returned array is compacted
                 $allnumericnames = true;
                 foreach ($atom_structure['subatoms'] as $subatomarray) {
                     if (!is_integer($subatomarray['name']) || count($subatomarray['subatoms']) != 1) {
                         $allnumericnames = false;
                         break;
                     }
                 }
                 if ($allnumericnames) {
                     $newData = array();
                     foreach ($atom_structure['subatoms'] as $subatomarray) {
                         foreach ($subatomarray['subatoms'] as $newData_subatomarray) {
                             unset($newData_subatomarray['hierarchy'], $newData_subatomarray['name']);
                             $newData[$subatomarray['name']] = $newData_subatomarray;
                             break;
                         }
                     }
                     $atom_structure['data'] = $newData;
                     unset($atom_structure['subatoms']);
                 }
             }
             break;
         case "":
         case "":
         case "":
         case "":
         case "":
             $atomname = getid3_lib::BigEndian2Int($atomname);
             $atom_structure['name'] = $atomname;
             $atom_structure['subatoms'] = $this->QuicktimeParseContainerAtom($atom_data, $baseoffset + 8, $atomHierarchy, $ParseAllPossibleAtoms);
             break;
         case 'stbl':
             // Sample TaBLe container atom
             $atom_structure['subatoms'] = $this->QuicktimeParseContainerAtom($atom_data, $baseoffset + 8, $atomHierarchy, $ParseAllPossibleAtoms);
             $isVideo = false;
             $framerate = 0;
             $framecount = 0;
             foreach ($atom_structure['subatoms'] as $key => $value_array) {
                 if (isset($value_array['sample_description_table'])) {
                     foreach ($value_array['sample_description_table'] as $key2 => $value_array2) {
                         if (isset($value_array2['data_format'])) {
                             switch ($value_array2['data_format']) {
                                 case 'avc1':
                                 case 'mp4v':
                                     // video data
                                     $isVideo = true;
                                     break;
                                 case 'mp4a':
                                     // audio data
                                     break;
                             }
                         }
                     }
                 } elseif (isset($value_array['time_to_sample_table'])) {
                     foreach ($value_array['time_to_sample_table'] as $key2 => $value_array2) {
                         if (isset($value_array2['sample_count']) && isset($value_array2['sample_duration']) && $value_array2['sample_duration'] > 0) {
                             $framerate = round($info['quicktime']['time_scale'] / $value_array2['sample_duration'], 3);
                             $framecount = $value_array2['sample_count'];
                         }
                     }
                 }
             }
             if ($isVideo && $framerate) {
                 $info['quicktime']['video']['frame_rate'] = $framerate;
                 $info['video']['frame_rate'] = $info['quicktime']['video']['frame_rate'];
             }
             if ($isVideo && $framecount) {
                 $info['quicktime']['video']['frame_count'] = $framecount;
             }
             break;
         case 'aART':
             // Album ARTist
         // Album ARTist
         case 'catg':
             // CaTeGory
         // CaTeGory
         case 'covr':
             // COVeR artwork
         // COVeR artwork
         case 'cpil':
             // ComPILation
         // ComPILation
         case 'cprt':
             // CoPyRighT
         // CoPyRighT
         case 'desc':
             // DESCription
         // DESCription
         case 'disk':
             // DISK number
         // DISK number
         case 'egid':
             // Episode Global ID
         // Episode Global ID
         case 'gnre':
             // GeNRE
         // GeNRE
         case 'keyw':
             // KEYWord
         // KEYWord
         case 'ldes':
         case 'pcst':
             // PodCaST
         // PodCaST
         case 'pgap':
             // GAPless Playback
         // GAPless Playback
         case 'purd':
             // PURchase Date
         // PURchase Date
         case 'purl':
             // Podcast URL
         // Podcast URL
         case 'rati':
         case 'rndu':
         case 'rpdu':
         case 'rtng':
             // RaTiNG
         // RaTiNG
         case 'stik':
         case 'tmpo':
             // TeMPO (BPM)
         // TeMPO (BPM)
         case 'trkn':
             // TRacK Number
         // TRacK Number
         case 'tves':
             // TV EpiSode
         // TV EpiSode
         case 'tvnn':
             // TV Network Name
         // TV Network Name
         case 'tvsh':
             // TV SHow Name
         // TV SHow Name
         case 'tvsn':
             // TV SeasoN
         // TV SeasoN
         case 'akID':
             // iTunes store account type
         // iTunes store account type
         case 'apID':
         case 'atID':
         case 'cmID':
         case 'cnID':
         case 'geID':
         case 'plID':
         case 'sfID':
             // iTunes store country
         // iTunes store country
         case "©" . 'alb':
             // ALBum
         // ALBum
         case "©" . 'art':
             // ARTist
         // ARTist
         case "©" . 'ART':
         case "©" . 'aut':
         case "©" . 'cmt':
             // CoMmenT
         // CoMmenT
         case "©" . 'com':
             // COMposer
         // COMposer
         case "©" . 'cpy':
         case "©" . 'day':
             // content created year
         // content created year
         case "©" . 'dir':
         case "©" . 'ed1':
         case "©" . 'ed2':
         case "©" . 'ed3':
         case "©" . 'ed4':
         case "©" . 'ed5':
         case "©" . 'ed6':
         case "©" . 'ed7':
         case "©" . 'ed8':
         case "©" . 'ed9':
         case "©" . 'enc':
         case "©" . 'fmt':
         case "©" . 'gen':
             // GENre
         // GENre
         case "©" . 'grp':
             // GRouPing
         // GRouPing
         case "©" . 'hst':
         case "©" . 'inf':
         case "©" . 'lyr':
             // LYRics
         // LYRics
         case "©" . 'mak':
         case "©" . 'mod':
         case "©" . 'nam':
             // full NAMe
         // full NAMe
         case "©" . 'ope':
         case "©" . 'PRD':
         case "©" . 'prd':
         case "©" . 'prf':
         case "©" . 'req':
         case "©" . 'src':
         case "©" . 'swr':
         case "©" . 'too':
             // encoder
         // encoder
         case "©" . 'trk':
             // TRacK
         // TRacK
         case "©" . 'url':
         case "©" . 'wrn':
         case "©" . 'wrt':
             // WRiTer
         // WRiTer
         case '----':
             // itunes specific
             if ($atom_parent == 'udta') {
                 // User data atom handler
                 $atom_structure['data_length'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 2));
                 $atom_structure['language_id'] = getid3_lib::BigEndian2Int(substr($atom_data, 2, 2));
                 $atom_structure['data'] = substr($atom_data, 4);
                 $atom_structure['language'] = $this->QuicktimeLanguageLookup($atom_structure['language_id']);
                 if (empty($info['comments']['language']) || !in_array($atom_structure['language'], $info['comments']['language'])) {
                     $info['comments']['language'][] = $atom_structure['language'];
                 }
             } else {
                 // Apple item list box atom handler
                 $atomoffset = 0;
                 if (substr($atom_data, 2, 2) == "µ") {
                     // not sure what it means, but observed on iPhone4 data.
                     // Each $atom_data has 2 bytes of datasize, plus 0x10B5, then data
                     while ($atomoffset < strlen($atom_data)) {
                         $boxsmallsize = getid3_lib::BigEndian2Int(substr($atom_data, $atomoffset, 2));
                         $boxsmalltype = substr($atom_data, $atomoffset + 2, 2);
                         $boxsmalldata = substr($atom_data, $atomoffset + 4, $boxsmallsize);
                         if ($boxsmallsize <= 1) {
                             $info['warning'][] = 'Invalid QuickTime atom smallbox size "' . $boxsmallsize . '" in atom "' . $atomname . '" at offset: ' . ($atom_structure['offset'] + $atomoffset);
                             $atom_structure['data'] = null;
                             $atomoffset = strlen($atom_data);
                             break;
                         }
                         switch ($boxsmalltype) {
                             case "µ":
                                 $atom_structure['data'] = $boxsmalldata;
                                 break;
                             default:
                                 $info['warning'][] = 'Unknown QuickTime smallbox type: "' . getid3_lib::PrintHexBytes($boxsmalltype) . '" at offset ' . $baseoffset;
                                 $atom_structure['data'] = $atom_data;
                                 break;
                         }
                         $atomoffset += 4 + $boxsmallsize;
                     }
                 } else {
                     while ($atomoffset < strlen($atom_data)) {
                         $boxsize = getid3_lib::BigEndian2Int(substr($atom_data, $atomoffset, 4));
                         $boxtype = substr($atom_data, $atomoffset + 4, 4);
                         $boxdata = substr($atom_data, $atomoffset + 8, $boxsize - 8);
                         if ($boxsize <= 1) {
                             $info['warning'][] = 'Invalid QuickTime atom box size "' . $boxsize . '" in atom "' . $atomname . '" at offset: ' . ($atom_structure['offset'] + $atomoffset);
                             $atom_structure['data'] = null;
                             $atomoffset = strlen($atom_data);
                             break;
                         }
                         $atomoffset += $boxsize;
                         switch ($boxtype) {
                             case 'mean':
                             case 'name':
                                 $atom_structure[$boxtype] = substr($boxdata, 4);
                                 break;
                             case 'data':
                                 $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($boxdata, 0, 1));
                                 $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($boxdata, 1, 3));
                                 switch ($atom_structure['flags_raw']) {
                                     case 0:
                                         // data flag
                                     // data flag
                                     case 21:
                                         // tmpo/cpil flag
                                         switch ($atomname) {
                                             case 'cpil':
                                             case 'pcst':
                                             case 'pgap':
                                                 $atom_structure['data'] = getid3_lib::BigEndian2Int(substr($boxdata, 8, 1));
                                                 break;
                                             case 'tmpo':
                                                 $atom_structure['data'] = getid3_lib::BigEndian2Int(substr($boxdata, 8, 2));
                                                 break;
                                             case 'disk':
                                             case 'trkn':
                                                 $num = getid3_lib::BigEndian2Int(substr($boxdata, 10, 2));
                                                 $num_total = getid3_lib::BigEndian2Int(substr($boxdata, 12, 2));
                                                 $atom_structure['data'] = empty($num) ? '' : $num;
                                                 $atom_structure['data'] .= empty($num_total) ? '' : '/' . $num_total;
                                                 break;
                                             case 'gnre':
                                                 $GenreID = getid3_lib::BigEndian2Int(substr($boxdata, 8, 4));
                                                 $atom_structure['data'] = getid3_id3v1::LookupGenreName($GenreID - 1);
                                                 break;
                                             case 'rtng':
                                                 $atom_structure[$atomname] = getid3_lib::BigEndian2Int(substr($boxdata, 8, 1));
                                                 $atom_structure['data'] = $this->QuicktimeContentRatingLookup($atom_structure[$atomname]);
                                                 break;
                                             case 'stik':
                                                 $atom_structure[$atomname] = getid3_lib::BigEndian2Int(substr($boxdata, 8, 1));
                                                 $atom_structure['data'] = $this->QuicktimeSTIKLookup($atom_structure[$atomname]);
                                                 break;
                                             case 'sfID':
                                                 $atom_structure[$atomname] = getid3_lib::BigEndian2Int(substr($boxdata, 8, 4));
                                                 $atom_structure['data'] = $this->QuicktimeStoreFrontCodeLookup($atom_structure[$atomname]);
                                                 break;
                                             case 'egid':
                                             case 'purl':
                                                 $atom_structure['data'] = substr($boxdata, 8);
                                                 break;
                                             default:
                                                 $atom_structure['data'] = getid3_lib::BigEndian2Int(substr($boxdata, 8, 4));
                                         }
                                         break;
                                     case 1:
                                         // text flag
                                     // text flag
                                     case 13:
                                         // image flag
                                     // image flag
                                     default:
                                         $atom_structure['data'] = substr($boxdata, 8);
                                         if ($atomname == 'covr') {
                                             // not a foolproof check, but better than nothing
                                             if (preg_match('#^\\xFF\\xD8\\xFF#', $atom_structure['data'])) {
                                                 $atom_structure['image_mime'] = 'image/jpeg';
                                             } elseif (preg_match('#^\\x89\\x50\\x4E\\x47\\x0D\\x0A\\x1A\\x0A#', $atom_structure['data'])) {
                                                 $atom_structure['image_mime'] = 'image/png';
                                             } elseif (preg_match('#^GIF#', $atom_structure['data'])) {
                                                 $atom_structure['image_mime'] = 'image/gif';
                                             }
                                         }
                                         break;
                                 }
                                 break;
                             default:
                                 $info['warning'][] = 'Unknown QuickTime box type: "' . getid3_lib::PrintHexBytes($boxtype) . '" at offset ' . $baseoffset;
                                 $atom_structure['data'] = $atom_data;
                         }
                     }
                 }
             }
             $this->CopyToAppropriateCommentsSection($atomname, $atom_structure['data'], $atom_structure['name']);
             break;
         case 'play':
             // auto-PLAY atom
             $atom_structure['autoplay'] = (bool) getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
             $info['quicktime']['autoplay'] = $atom_structure['autoplay'];
             break;
         case 'WLOC':
             // Window LOCation atom
             $atom_structure['location_x'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 2));
             $atom_structure['location_y'] = getid3_lib::BigEndian2Int(substr($atom_data, 2, 2));
             break;
         case 'LOOP':
             // LOOPing atom
         // LOOPing atom
         case 'SelO':
             // play SELection Only atom
         // play SELection Only atom
         case 'AllF':
             // play ALL Frames atom
             $atom_structure['data'] = getid3_lib::BigEndian2Int($atom_data);
             break;
         case 'name':
             //
         //
         case 'MCPS':
             // Media Cleaner PRo
         // Media Cleaner PRo
         case '@PRM':
             // adobe PReMiere version
         // adobe PReMiere version
         case '@PRQ':
             // adobe PRemiere Quicktime version
             $atom_structure['data'] = $atom_data;
             break;
         case 'cmvd':
             // Compressed MooV Data atom
             // Code by ubergeekØubergeek*tv based on information from
             // http://developer.apple.com/quicktime/icefloe/dispatch012.html
             $atom_structure['unCompressedSize'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 4));
             $CompressedFileData = substr($atom_data, 4);
             if ($UncompressedHeader = @gzuncompress($CompressedFileData)) {
                 $atom_structure['subatoms'] = $this->QuicktimeParseContainerAtom($UncompressedHeader, 0, $atomHierarchy, $ParseAllPossibleAtoms);
             } else {
                 $info['warning'][] = 'Error decompressing compressed MOV atom at offset ' . $atom_structure['offset'];
             }
             break;
         case 'dcom':
             // Data COMpression atom
             $atom_structure['compression_id'] = $atom_data;
             $atom_structure['compression_text'] = $this->QuicktimeDCOMLookup($atom_data);
             break;
         case 'rdrf':
             // Reference movie Data ReFerence atom
             $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
             $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3));
             $atom_structure['flags']['internal_data'] = (bool) ($atom_structure['flags_raw'] & 0x1);
             $atom_structure['reference_type_name'] = substr($atom_data, 4, 4);
             $atom_structure['reference_length'] = getid3_lib::BigEndian2Int(substr($atom_data, 8, 4));
             switch ($atom_structure['reference_type_name']) {
                 case 'url ':
                     $atom_structure['url'] = $this->NoNullString(substr($atom_data, 12));
                     break;
                 case 'alis':
                     $atom_structure['file_alias'] = substr($atom_data, 12);
                     break;
                 case 'rsrc':
                     $atom_structure['resource_alias'] = substr($atom_data, 12);
                     break;
                 default:
                     $atom_structure['data'] = substr($atom_data, 12);
                     break;
             }
             break;
         case 'rmqu':
             // Reference Movie QUality atom
             $atom_structure['movie_quality'] = getid3_lib::BigEndian2Int($atom_data);
             break;
         case 'rmcs':
             // Reference Movie Cpu Speed atom
             $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
             $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3));
             // hardcoded: 0x0000
             $atom_structure['cpu_speed_rating'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 2));
             break;
         case 'rmvc':
             // Reference Movie Version Check atom
             $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
             $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3));
             // hardcoded: 0x0000
             $atom_structure['gestalt_selector'] = substr($atom_data, 4, 4);
             $atom_structure['gestalt_value_mask'] = getid3_lib::BigEndian2Int(substr($atom_data, 8, 4));
             $atom_structure['gestalt_value'] = getid3_lib::BigEndian2Int(substr($atom_data, 12, 4));
             $atom_structure['gestalt_check_type'] = getid3_lib::BigEndian2Int(substr($atom_data, 14, 2));
             break;
         case 'rmcd':
             // Reference Movie Component check atom
             $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
             $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3));
             // hardcoded: 0x0000
             $atom_structure['component_type'] = substr($atom_data, 4, 4);
             $atom_structure['component_subtype'] = substr($atom_data, 8, 4);
             $atom_structure['component_manufacturer'] = substr($atom_data, 12, 4);
             $atom_structure['component_flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 16, 4));
             $atom_structure['component_flags_mask'] = getid3_lib::BigEndian2Int(substr($atom_data, 20, 4));
             $atom_structure['component_min_version'] = getid3_lib::BigEndian2Int(substr($atom_data, 24, 4));
             break;
         case 'rmdr':
             // Reference Movie Data Rate atom
             $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
             $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3));
             // hardcoded: 0x0000
             $atom_structure['data_rate'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4));
             $atom_structure['data_rate_bps'] = $atom_structure['data_rate'] * 10;
             break;
         case 'rmla':
             // Reference Movie Language Atom
             $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
             $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3));
             // hardcoded: 0x0000
             $atom_structure['language_id'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 2));
             $atom_structure['language'] = $this->QuicktimeLanguageLookup($atom_structure['language_id']);
             if (empty($info['comments']['language']) || !in_array($atom_structure['language'], $info['comments']['language'])) {
                 $info['comments']['language'][] = $atom_structure['language'];
             }
             break;
         case 'rmla':
             // Reference Movie Language Atom
             $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
             $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3));
             // hardcoded: 0x0000
             $atom_structure['track_id'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 2));
             break;
         case 'ptv ':
             // Print To Video - defines a movie's full screen mode
             // http://developer.apple.com/documentation/QuickTime/APIREF/SOURCESIV/at_ptv-_pg.htm
             $atom_structure['display_size_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 2));
             $atom_structure['reserved_1'] = getid3_lib::BigEndian2Int(substr($atom_data, 2, 2));
             // hardcoded: 0x0000
             $atom_structure['reserved_2'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 2));
             // hardcoded: 0x0000
             $atom_structure['slide_show_flag'] = getid3_lib::BigEndian2Int(substr($atom_data, 6, 1));
             $atom_structure['play_on_open_flag'] = getid3_lib::BigEndian2Int(substr($atom_data, 7, 1));
             $atom_structure['flags']['play_on_open'] = (bool) $atom_structure['play_on_open_flag'];
             $atom_structure['flags']['slide_show'] = (bool) $atom_structure['slide_show_flag'];
             $ptv_lookup[0] = 'normal';
             $ptv_lookup[1] = 'double';
             $ptv_lookup[2] = 'half';
             $ptv_lookup[3] = 'full';
             $ptv_lookup[4] = 'current';
             if (isset($ptv_lookup[$atom_structure['display_size_raw']])) {
                 $atom_structure['display_size'] = $ptv_lookup[$atom_structure['display_size_raw']];
             } else {
                 $info['warning'][] = 'unknown "ptv " display constant (' . $atom_structure['display_size_raw'] . ')';
             }
             break;
         case 'stsd':
             // Sample Table Sample Description atom
             $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
             $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3));
             // hardcoded: 0x0000
             $atom_structure['number_entries'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4));
             $stsdEntriesDataOffset = 8;
             for ($i = 0; $i < $atom_structure['number_entries']; $i++) {
                 $atom_structure['sample_description_table'][$i]['size'] = getid3_lib::BigEndian2Int(substr($atom_data, $stsdEntriesDataOffset, 4));
                 $stsdEntriesDataOffset += 4;
                 $atom_structure['sample_description_table'][$i]['data_format'] = substr($atom_data, $stsdEntriesDataOffset, 4);
                 $stsdEntriesDataOffset += 4;
                 $atom_structure['sample_description_table'][$i]['reserved'] = getid3_lib::BigEndian2Int(substr($atom_data, $stsdEntriesDataOffset, 6));
                 $stsdEntriesDataOffset += 6;
                 $atom_structure['sample_description_table'][$i]['reference_index'] = getid3_lib::BigEndian2Int(substr($atom_data, $stsdEntriesDataOffset, 2));
                 $stsdEntriesDataOffset += 2;
                 $atom_structure['sample_description_table'][$i]['data'] = substr($atom_data, $stsdEntriesDataOffset, $atom_structure['sample_description_table'][$i]['size'] - 4 - 4 - 6 - 2);
                 $stsdEntriesDataOffset += $atom_structure['sample_description_table'][$i]['size'] - 4 - 4 - 6 - 2;
                 $atom_structure['sample_description_table'][$i]['encoder_version'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 0, 2));
                 $atom_structure['sample_description_table'][$i]['encoder_revision'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 2, 2));
                 $atom_structure['sample_description_table'][$i]['encoder_vendor'] = substr($atom_structure['sample_description_table'][$i]['data'], 4, 4);
                 switch ($atom_structure['sample_description_table'][$i]['encoder_vendor']) {
                     case "":
                         // audio tracks
                         $atom_structure['sample_description_table'][$i]['audio_channels'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 8, 2));
                         $atom_structure['sample_description_table'][$i]['audio_bit_depth'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 10, 2));
                         $atom_structure['sample_description_table'][$i]['audio_compression_id'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 12, 2));
                         $atom_structure['sample_description_table'][$i]['audio_packet_size'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 14, 2));
                         $atom_structure['sample_description_table'][$i]['audio_sample_rate'] = getid3_lib::FixedPoint16_16(substr($atom_structure['sample_description_table'][$i]['data'], 16, 4));
                         // video tracks
                         // http://developer.apple.com/library/mac/#documentation/QuickTime/QTFF/QTFFChap3/qtff3.html
                         $atom_structure['sample_description_table'][$i]['temporal_quality'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 8, 4));
                         $atom_structure['sample_description_table'][$i]['spatial_quality'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 12, 4));
                         $atom_structure['sample_description_table'][$i]['width'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 16, 2));
                         $atom_structure['sample_description_table'][$i]['height'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 18, 2));
                         $atom_structure['sample_description_table'][$i]['resolution_x'] = getid3_lib::FixedPoint16_16(substr($atom_structure['sample_description_table'][$i]['data'], 24, 4));
                         $atom_structure['sample_description_table'][$i]['resolution_y'] = getid3_lib::FixedPoint16_16(substr($atom_structure['sample_description_table'][$i]['data'], 28, 4));
                         $atom_structure['sample_description_table'][$i]['data_size'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 32, 4));
                         $atom_structure['sample_description_table'][$i]['frame_count'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 36, 2));
                         $atom_structure['sample_description_table'][$i]['compressor_name'] = substr($atom_structure['sample_description_table'][$i]['data'], 38, 4);
                         $atom_structure['sample_description_table'][$i]['pixel_depth'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 42, 2));
                         $atom_structure['sample_description_table'][$i]['color_table_id'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 44, 2));
                         switch ($atom_structure['sample_description_table'][$i]['data_format']) {
                             case '2vuY':
                             case 'avc1':
                             case 'cvid':
                             case 'dvc ':
                             case 'dvcp':
                             case 'gif ':
                             case 'h263':
                             case 'jpeg':
                             case 'kpcd':
                             case 'mjpa':
                             case 'mjpb':
                             case 'mp4v':
                             case 'png ':
                             case 'raw ':
                             case 'rle ':
                             case 'rpza':
                             case 'smc ':
                             case 'SVQ1':
                             case 'SVQ3':
                             case 'tiff':
                             case 'v210':
                             case 'v216':
                             case 'v308':
                             case 'v408':
                             case 'v410':
                             case 'yuv2':
                                 $info['fileformat'] = 'mp4';
                                 $info['video']['fourcc'] = $atom_structure['sample_description_table'][$i]['data_format'];
                                 // http://www.getid3.org/phpBB3/viewtopic.php?t=1550
                                 //if ((!empty($atom_structure['sample_description_table'][$i]['width']) && !empty($atom_structure['sample_description_table'][$i]['width'])) && (empty($info['video']['resolution_x']) || empty($info['video']['resolution_y']) || (number_format($info['video']['resolution_x'], 6) != number_format(round($info['video']['resolution_x']), 6)) || (number_format($info['video']['resolution_y'], 6) != number_format(round($info['video']['resolution_y']), 6)))) { // ugly check for floating point numbers
                                 if (!empty($atom_structure['sample_description_table'][$i]['width']) && !empty($atom_structure['sample_description_table'][$i]['height'])) {
                                     // assume that values stored here are more important than values stored in [tkhd] atom
                                     $info['video']['resolution_x'] = $atom_structure['sample_description_table'][$i]['width'];
                                     $info['video']['resolution_y'] = $atom_structure['sample_description_table'][$i]['height'];
                                     $info['quicktime']['video']['resolution_x'] = $info['video']['resolution_x'];
                                     $info['quicktime']['video']['resolution_y'] = $info['video']['resolution_y'];
                                 }
                                 break;
                             case 'qtvr':
                                 $info['video']['dataformat'] = 'quicktimevr';
                                 break;
                             case 'mp4a':
                             default:
                                 $info['quicktime']['audio']['codec'] = $this->QuicktimeAudioCodecLookup($atom_structure['sample_description_table'][$i]['data_format']);
                                 $info['quicktime']['audio']['sample_rate'] = $atom_structure['sample_description_table'][$i]['audio_sample_rate'];
                                 $info['quicktime']['audio']['channels'] = $atom_structure['sample_description_table'][$i]['audio_channels'];
                                 $info['quicktime']['audio']['bit_depth'] = $atom_structure['sample_description_table'][$i]['audio_bit_depth'];
                                 $info['audio']['codec'] = $info['quicktime']['audio']['codec'];
                                 $info['audio']['sample_rate'] = $info['quicktime']['audio']['sample_rate'];
                                 $info['audio']['channels'] = $info['quicktime']['audio']['channels'];
                                 $info['audio']['bits_per_sample'] = $info['quicktime']['audio']['bit_depth'];
                                 switch ($atom_structure['sample_description_table'][$i]['data_format']) {
                                     case 'raw ':
                                         // PCM
                                     // PCM
                                     case 'alac':
                                         // Apple Lossless Audio Codec
                                         $info['audio']['lossless'] = true;
                                         break;
                                     default:
                                         $info['audio']['lossless'] = false;
                                         break;
                                 }
                                 break;
                         }
                         break;
                     default:
                         switch ($atom_structure['sample_description_table'][$i]['data_format']) {
                             case 'mp4s':
                                 $info['fileformat'] = 'mp4';
                                 break;
                             default:
                                 // video atom
                                 $atom_structure['sample_description_table'][$i]['video_temporal_quality'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 8, 4));
                                 $atom_structure['sample_description_table'][$i]['video_spatial_quality'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 12, 4));
                                 $atom_structure['sample_description_table'][$i]['video_frame_width'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 16, 2));
                                 $atom_structure['sample_description_table'][$i]['video_frame_height'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 18, 2));
                                 $atom_structure['sample_description_table'][$i]['video_resolution_x'] = getid3_lib::FixedPoint16_16(substr($atom_structure['sample_description_table'][$i]['data'], 20, 4));
                                 $atom_structure['sample_description_table'][$i]['video_resolution_y'] = getid3_lib::FixedPoint16_16(substr($atom_structure['sample_description_table'][$i]['data'], 24, 4));
                                 $atom_structure['sample_description_table'][$i]['video_data_size'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 28, 4));
                                 $atom_structure['sample_description_table'][$i]['video_frame_count'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 32, 2));
                                 $atom_structure['sample_description_table'][$i]['video_encoder_name_len'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 34, 1));
                                 $atom_structure['sample_description_table'][$i]['video_encoder_name'] = substr($atom_structure['sample_description_table'][$i]['data'], 35, $atom_structure['sample_description_table'][$i]['video_encoder_name_len']);
                                 $atom_structure['sample_description_table'][$i]['video_pixel_color_depth'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 66, 2));
                                 $atom_structure['sample_description_table'][$i]['video_color_table_id'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 68, 2));
                                 $atom_structure['sample_description_table'][$i]['video_pixel_color_type'] = $atom_structure['sample_description_table'][$i]['video_pixel_color_depth'] > 32 ? 'grayscale' : 'color';
                                 $atom_structure['sample_description_table'][$i]['video_pixel_color_name'] = $this->QuicktimeColorNameLookup($atom_structure['sample_description_table'][$i]['video_pixel_color_depth']);
                                 if ($atom_structure['sample_description_table'][$i]['video_pixel_color_name'] != 'invalid') {
                                     $info['quicktime']['video']['codec_fourcc'] = $atom_structure['sample_description_table'][$i]['data_format'];
                                     $info['quicktime']['video']['codec_fourcc_lookup'] = $this->QuicktimeVideoCodecLookup($atom_structure['sample_description_table'][$i]['data_format']);
                                     $info['quicktime']['video']['codec'] = $atom_structure['sample_description_table'][$i]['video_encoder_name_len'] > 0 ? $atom_structure['sample_description_table'][$i]['video_encoder_name'] : $atom_structure['sample_description_table'][$i]['data_format'];
                                     $info['quicktime']['video']['color_depth'] = $atom_structure['sample_description_table'][$i]['video_pixel_color_depth'];
                                     $info['quicktime']['video']['color_depth_name'] = $atom_structure['sample_description_table'][$i]['video_pixel_color_name'];
                                     $info['video']['codec'] = $info['quicktime']['video']['codec'];
                                     $info['video']['bits_per_sample'] = $info['quicktime']['video']['color_depth'];
                                 }
                                 $info['video']['lossless'] = false;
                                 $info['video']['pixel_aspect_ratio'] = (double) 1;
                                 break;
                         }
                         break;
                 }
                 switch (strtolower($atom_structure['sample_description_table'][$i]['data_format'])) {
                     case 'mp4a':
                         $info['audio']['dataformat'] = 'mp4';
                         $info['quicktime']['audio']['codec'] = 'mp4';
                         break;
                     case '3ivx':
                     case '3iv1':
                     case '3iv2':
                         $info['video']['dataformat'] = '3ivx';
                         break;
                     case 'xvid':
                         $info['video']['dataformat'] = 'xvid';
                         break;
                     case 'mp4v':
                         $info['video']['dataformat'] = 'mpeg4';
                         break;
                     case 'divx':
                     case 'div1':
                     case 'div2':
                     case 'div3':
                     case 'div4':
                     case 'div5':
                     case 'div6':
                         $info['video']['dataformat'] = 'divx';
                         break;
                     default:
                         // do nothing
                         break;
                 }
                 unset($atom_structure['sample_description_table'][$i]['data']);
             }
             break;
         case 'stts':
             // Sample Table Time-to-Sample atom
             $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
             $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3));
             // hardcoded: 0x0000
             $atom_structure['number_entries'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4));
             $sttsEntriesDataOffset = 8;
             //$FrameRateCalculatorArray = array();
             $frames_count = 0;
             for ($i = 0; $i < $atom_structure['number_entries']; $i++) {
                 $atom_structure['time_to_sample_table'][$i]['sample_count'] = getid3_lib::BigEndian2Int(substr($atom_data, $sttsEntriesDataOffset, 4));
                 $sttsEntriesDataOffset += 4;
                 $atom_structure['time_to_sample_table'][$i]['sample_duration'] = getid3_lib::BigEndian2Int(substr($atom_data, $sttsEntriesDataOffset, 4));
                 $sttsEntriesDataOffset += 4;
                 $frames_count += $atom_structure['time_to_sample_table'][$i]['sample_count'];
                 // THIS SECTION REPLACED WITH CODE IN "stbl" ATOM
                 //if (!empty($info['quicktime']['time_scale']) && ($atom_structure['time_to_sample_table'][$i]['sample_duration'] > 0)) {
                 //	$stts_new_framerate = $info['quicktime']['time_scale'] / $atom_structure['time_to_sample_table'][$i]['sample_duration'];
                 //	if ($stts_new_framerate <= 60) {
                 //		// some atoms have durations of "1" giving a very large framerate, which probably is not right
                 //		$info['video']['frame_rate'] = max($info['video']['frame_rate'], $stts_new_framerate);
                 //	}
                 //}
                 //
                 //$FrameRateCalculatorArray[($info['quicktime']['time_scale'] / $atom_structure['time_to_sample_table'][$i]['sample_duration'])] += $atom_structure['time_to_sample_table'][$i]['sample_count'];
             }
             $info['quicktime']['stts_framecount'][] = $frames_count;
             //$sttsFramesTotal  = 0;
             //$sttsSecondsTotal = 0;
             //foreach ($FrameRateCalculatorArray as $frames_per_second => $frame_count) {
             //	if (($frames_per_second > 60) || ($frames_per_second < 1)) {
             //		// not video FPS information, probably audio information
             //		$sttsFramesTotal  = 0;
             //		$sttsSecondsTotal = 0;
             //		break;
             //	}
             //	$sttsFramesTotal  += $frame_count;
             //	$sttsSecondsTotal += $frame_count / $frames_per_second;
             //}
             //if (($sttsFramesTotal > 0) && ($sttsSecondsTotal > 0)) {
             //	if (($sttsFramesTotal / $sttsSecondsTotal) > $info['video']['frame_rate']) {
             //		$info['video']['frame_rate'] = $sttsFramesTotal / $sttsSecondsTotal;
             //	}
             //}
             break;
         case 'stss':
             // Sample Table Sync Sample (key frames) atom
             if ($ParseAllPossibleAtoms) {
                 $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
                 $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3));
                 // hardcoded: 0x0000
                 $atom_structure['number_entries'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4));
                 $stssEntriesDataOffset = 8;
                 for ($i = 0; $i < $atom_structure['number_entries']; $i++) {
                     $atom_structure['time_to_sample_table'][$i] = getid3_lib::BigEndian2Int(substr($atom_data, $stssEntriesDataOffset, 4));
                     $stssEntriesDataOffset += 4;
                 }
             }
             break;
         case 'stsc':
             // Sample Table Sample-to-Chunk atom
             if ($ParseAllPossibleAtoms) {
                 $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
                 $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3));
                 // hardcoded: 0x0000
                 $atom_structure['number_entries'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4));
                 $stscEntriesDataOffset = 8;
                 for ($i = 0; $i < $atom_structure['number_entries']; $i++) {
                     $atom_structure['sample_to_chunk_table'][$i]['first_chunk'] = getid3_lib::BigEndian2Int(substr($atom_data, $stscEntriesDataOffset, 4));
                     $stscEntriesDataOffset += 4;
                     $atom_structure['sample_to_chunk_table'][$i]['samples_per_chunk'] = getid3_lib::BigEndian2Int(substr($atom_data, $stscEntriesDataOffset, 4));
                     $stscEntriesDataOffset += 4;
                     $atom_structure['sample_to_chunk_table'][$i]['sample_description'] = getid3_lib::BigEndian2Int(substr($atom_data, $stscEntriesDataOffset, 4));
                     $stscEntriesDataOffset += 4;
                 }
             }
             break;
         case 'stsz':
             // Sample Table SiZe atom
             if ($ParseAllPossibleAtoms) {
                 $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
                 $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3));
                 // hardcoded: 0x0000
                 $atom_structure['sample_size'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4));
                 $atom_structure['number_entries'] = getid3_lib::BigEndian2Int(substr($atom_data, 8, 4));
                 $stszEntriesDataOffset = 12;
                 if ($atom_structure['sample_size'] == 0) {
                     for ($i = 0; $i < $atom_structure['number_entries']; $i++) {
                         $atom_structure['sample_size_table'][$i] = getid3_lib::BigEndian2Int(substr($atom_data, $stszEntriesDataOffset, 4));
                         $stszEntriesDataOffset += 4;
                     }
                 }
             }
             break;
         case 'stco':
             // Sample Table Chunk Offset atom
             if ($ParseAllPossibleAtoms) {
                 $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
                 $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3));
                 // hardcoded: 0x0000
                 $atom_structure['number_entries'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4));
                 $stcoEntriesDataOffset = 8;
                 for ($i = 0; $i < $atom_structure['number_entries']; $i++) {
                     $atom_structure['chunk_offset_table'][$i] = getid3_lib::BigEndian2Int(substr($atom_data, $stcoEntriesDataOffset, 4));
                     $stcoEntriesDataOffset += 4;
                 }
             }
             break;
         case 'co64':
             // Chunk Offset 64-bit (version of "stco" that supports > 2GB files)
             if ($ParseAllPossibleAtoms) {
                 $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
                 $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3));
                 // hardcoded: 0x0000
                 $atom_structure['number_entries'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4));
                 $stcoEntriesDataOffset = 8;
                 for ($i = 0; $i < $atom_structure['number_entries']; $i++) {
                     $atom_structure['chunk_offset_table'][$i] = getid3_lib::BigEndian2Int(substr($atom_data, $stcoEntriesDataOffset, 8));
                     $stcoEntriesDataOffset += 8;
                 }
             }
             break;
         case 'dref':
             // Data REFerence atom
             $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
             $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3));
             // hardcoded: 0x0000
             $atom_structure['number_entries'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4));
             $drefDataOffset = 8;
             for ($i = 0; $i < $atom_structure['number_entries']; $i++) {
                 $atom_structure['data_references'][$i]['size'] = getid3_lib::BigEndian2Int(substr($atom_data, $drefDataOffset, 4));
                 $drefDataOffset += 4;
                 $atom_structure['data_references'][$i]['type'] = substr($atom_data, $drefDataOffset, 4);
                 $drefDataOffset += 4;
                 $atom_structure['data_references'][$i]['version'] = getid3_lib::BigEndian2Int(substr($atom_data, $drefDataOffset, 1));
                 $drefDataOffset += 1;
                 $atom_structure['data_references'][$i]['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, $drefDataOffset, 3));
                 // hardcoded: 0x0000
                 $drefDataOffset += 3;
                 $atom_structure['data_references'][$i]['data'] = substr($atom_data, $drefDataOffset, $atom_structure['data_references'][$i]['size'] - 4 - 4 - 1 - 3);
                 $drefDataOffset += $atom_structure['data_references'][$i]['size'] - 4 - 4 - 1 - 3;
                 $atom_structure['data_references'][$i]['flags']['self_reference'] = (bool) ($atom_structure['data_references'][$i]['flags_raw'] & 0x1);
             }
             break;
         case 'gmin':
             // base Media INformation atom
             $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
             $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3));
             // hardcoded: 0x0000
             $atom_structure['graphics_mode'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 2));
             $atom_structure['opcolor_red'] = getid3_lib::BigEndian2Int(substr($atom_data, 6, 2));
             $atom_structure['opcolor_green'] = getid3_lib::BigEndian2Int(substr($atom_data, 8, 2));
             $atom_structure['opcolor_blue'] = getid3_lib::BigEndian2Int(substr($atom_data, 10, 2));
             $atom_structure['balance'] = getid3_lib::BigEndian2Int(substr($atom_data, 12, 2));
             $atom_structure['reserved'] = getid3_lib::BigEndian2Int(substr($atom_data, 14, 2));
             break;
         case 'smhd':
             // Sound Media information HeaDer atom
             $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
             $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3));
             // hardcoded: 0x0000
             $atom_structure['balance'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 2));
             $atom_structure['reserved'] = getid3_lib::BigEndian2Int(substr($atom_data, 6, 2));
             break;
         case 'vmhd':
             // Video Media information HeaDer atom
             $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
             $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3));
             $atom_structure['graphics_mode'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 2));
             $atom_structure['opcolor_red'] = getid3_lib::BigEndian2Int(substr($atom_data, 6, 2));
             $atom_structure['opcolor_green'] = getid3_lib::BigEndian2Int(substr($atom_data, 8, 2));
             $atom_structure['opcolor_blue'] = getid3_lib::BigEndian2Int(substr($atom_data, 10, 2));
             $atom_structure['flags']['no_lean_ahead'] = (bool) ($atom_structure['flags_raw'] & 0x1);
             break;
         case 'hdlr':
             // HanDLeR reference atom
             $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
             $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3));
             // hardcoded: 0x0000
             $atom_structure['component_type'] = substr($atom_data, 4, 4);
             $atom_structure['component_subtype'] = substr($atom_data, 8, 4);
             $atom_structure['component_manufacturer'] = substr($atom_data, 12, 4);
             $atom_structure['component_flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 16, 4));
             $atom_structure['component_flags_mask'] = getid3_lib::BigEndian2Int(substr($atom_data, 20, 4));
             $atom_structure['component_name'] = $this->Pascal2String(substr($atom_data, 24));
             if ($atom_structure['component_subtype'] == 'STpn' && $atom_structure['component_manufacturer'] == 'zzzz') {
                 $info['video']['dataformat'] = 'quicktimevr';
             }
             break;
         case 'mdhd':
             // MeDia HeaDer atom
             $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
             $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3));
             // hardcoded: 0x0000
             $atom_structure['creation_time'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4));
             $atom_structure['modify_time'] = getid3_lib::BigEndian2Int(substr($atom_data, 8, 4));
             $atom_structure['time_scale'] = getid3_lib::BigEndian2Int(substr($atom_data, 12, 4));
             $atom_structure['duration'] = getid3_lib::BigEndian2Int(substr($atom_data, 16, 4));
             $atom_structure['language_id'] = getid3_lib::BigEndian2Int(substr($atom_data, 20, 2));
             $atom_structure['quality'] = getid3_lib::BigEndian2Int(substr($atom_data, 22, 2));
             if ($atom_structure['time_scale'] == 0) {
                 $info['error'][] = 'Corrupt Quicktime file: mdhd.time_scale == zero';
                 return false;
             }
             $info['quicktime']['time_scale'] = isset($info['quicktime']['time_scale']) ? max($info['quicktime']['time_scale'], $atom_structure['time_scale']) : $atom_structure['time_scale'];
             $atom_structure['creation_time_unix'] = getid3_lib::DateMac2Unix($atom_structure['creation_time']);
             $atom_structure['modify_time_unix'] = getid3_lib::DateMac2Unix($atom_structure['modify_time']);
             $atom_structure['playtime_seconds'] = $atom_structure['duration'] / $atom_structure['time_scale'];
             $atom_structure['language'] = $this->QuicktimeLanguageLookup($atom_structure['language_id']);
             if (empty($info['comments']['language']) || !in_array($atom_structure['language'], $info['comments']['language'])) {
                 $info['comments']['language'][] = $atom_structure['language'];
             }
             break;
         case 'pnot':
             // Preview atom
             $atom_structure['modification_date'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 4));
             // "standard Macintosh format"
             $atom_structure['version_number'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 2));
             // hardcoded: 0x00
             $atom_structure['atom_type'] = substr($atom_data, 6, 4);
             // usually: 'PICT'
             $atom_structure['atom_index'] = getid3_lib::BigEndian2Int(substr($atom_data, 10, 2));
             // usually: 0x01
             $atom_structure['modification_date_unix'] = getid3_lib::DateMac2Unix($atom_structure['modification_date']);
             break;
         case 'crgn':
             // Clipping ReGioN atom
             $atom_structure['region_size'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 2));
             // The Region size, Region boundary box,
             $atom_structure['boundary_box'] = getid3_lib::BigEndian2Int(substr($atom_data, 2, 8));
             // and Clipping region data fields
             $atom_structure['clipping_data'] = substr($atom_data, 10);
             // constitute a QuickDraw region.
             break;
         case 'load':
             // track LOAD settings atom
             $atom_structure['preload_start_time'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 4));
             $atom_structure['preload_duration'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4));
             $atom_structure['preload_flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 8, 4));
             $atom_structure['default_hints_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 12, 4));
             $atom_structure['default_hints']['double_buffer'] = (bool) ($atom_structure['default_hints_raw'] & 0x20);
             $atom_structure['default_hints']['high_quality'] = (bool) ($atom_structure['default_hints_raw'] & 0x100);
             break;
         case 'tmcd':
             // TiMe CoDe atom
         // TiMe CoDe atom
         case 'chap':
             // CHAPter list atom
         // CHAPter list atom
         case 'sync':
             // SYNChronization atom
         // SYNChronization atom
         case 'scpt':
             // tranSCriPT atom
         // tranSCriPT atom
         case 'ssrc':
             // non-primary SouRCe atom
             for ($i = 0; $i < strlen($atom_data); $i += 4) {
                 @($atom_structure['track_id'][] = getid3_lib::BigEndian2Int(substr($atom_data, $i, 4)));
             }
             break;
         case 'elst':
             // Edit LiST atom
             $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
             $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3));
             // hardcoded: 0x0000
             $atom_structure['number_entries'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4));
             for ($i = 0; $i < $atom_structure['number_entries']; $i++) {
                 $atom_structure['edit_list'][$i]['track_duration'] = getid3_lib::BigEndian2Int(substr($atom_data, 8 + $i * 12 + 0, 4));
                 $atom_structure['edit_list'][$i]['media_time'] = getid3_lib::BigEndian2Int(substr($atom_data, 8 + $i * 12 + 4, 4));
                 $atom_structure['edit_list'][$i]['media_rate'] = getid3_lib::FixedPoint16_16(substr($atom_data, 8 + $i * 12 + 8, 4));
             }
             break;
         case 'kmat':
             // compressed MATte atom
             $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
             $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3));
             // hardcoded: 0x0000
             $atom_structure['matte_data_raw'] = substr($atom_data, 4);
             break;
         case 'ctab':
             // Color TABle atom
             $atom_structure['color_table_seed'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 4));
             // hardcoded: 0x00000000
             $atom_structure['color_table_flags'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 2));
             // hardcoded: 0x8000
             $atom_structure['color_table_size'] = getid3_lib::BigEndian2Int(substr($atom_data, 6, 2)) + 1;
             for ($colortableentry = 0; $colortableentry < $atom_structure['color_table_size']; $colortableentry++) {
                 $atom_structure['color_table'][$colortableentry]['alpha'] = getid3_lib::BigEndian2Int(substr($atom_data, 8 + $colortableentry * 8 + 0, 2));
                 $atom_structure['color_table'][$colortableentry]['red'] = getid3_lib::BigEndian2Int(substr($atom_data, 8 + $colortableentry * 8 + 2, 2));
                 $atom_structure['color_table'][$colortableentry]['green'] = getid3_lib::BigEndian2Int(substr($atom_data, 8 + $colortableentry * 8 + 4, 2));
                 $atom_structure['color_table'][$colortableentry]['blue'] = getid3_lib::BigEndian2Int(substr($atom_data, 8 + $colortableentry * 8 + 6, 2));
             }
             break;
         case 'mvhd':
             // MoVie HeaDer atom
             $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
             $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3));
             $atom_structure['creation_time'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4));
             $atom_structure['modify_time'] = getid3_lib::BigEndian2Int(substr($atom_data, 8, 4));
             $atom_structure['time_scale'] = getid3_lib::BigEndian2Int(substr($atom_data, 12, 4));
             $atom_structure['duration'] = getid3_lib::BigEndian2Int(substr($atom_data, 16, 4));
             $atom_structure['preferred_rate'] = getid3_lib::FixedPoint16_16(substr($atom_data, 20, 4));
             $atom_structure['preferred_volume'] = getid3_lib::FixedPoint8_8(substr($atom_data, 24, 2));
             $atom_structure['reserved'] = substr($atom_data, 26, 10);
             $atom_structure['matrix_a'] = getid3_lib::FixedPoint16_16(substr($atom_data, 36, 4));
             $atom_structure['matrix_b'] = getid3_lib::FixedPoint16_16(substr($atom_data, 40, 4));
             $atom_structure['matrix_u'] = getid3_lib::FixedPoint2_30(substr($atom_data, 44, 4));
             $atom_structure['matrix_c'] = getid3_lib::FixedPoint16_16(substr($atom_data, 48, 4));
             $atom_structure['matrix_d'] = getid3_lib::FixedPoint16_16(substr($atom_data, 52, 4));
             $atom_structure['matrix_v'] = getid3_lib::FixedPoint2_30(substr($atom_data, 56, 4));
             $atom_structure['matrix_x'] = getid3_lib::FixedPoint16_16(substr($atom_data, 60, 4));
             $atom_structure['matrix_y'] = getid3_lib::FixedPoint16_16(substr($atom_data, 64, 4));
             $atom_structure['matrix_w'] = getid3_lib::FixedPoint2_30(substr($atom_data, 68, 4));
             $atom_structure['preview_time'] = getid3_lib::BigEndian2Int(substr($atom_data, 72, 4));
             $atom_structure['preview_duration'] = getid3_lib::BigEndian2Int(substr($atom_data, 76, 4));
             $atom_structure['poster_time'] = getid3_lib::BigEndian2Int(substr($atom_data, 80, 4));
             $atom_structure['selection_time'] = getid3_lib::BigEndian2Int(substr($atom_data, 84, 4));
             $atom_structure['selection_duration'] = getid3_lib::BigEndian2Int(substr($atom_data, 88, 4));
             $atom_structure['current_time'] = getid3_lib::BigEndian2Int(substr($atom_data, 92, 4));
             $atom_structure['next_track_id'] = getid3_lib::BigEndian2Int(substr($atom_data, 96, 4));
             if ($atom_structure['time_scale'] == 0) {
                 $info['error'][] = 'Corrupt Quicktime file: mvhd.time_scale == zero';
                 return false;
             }
             $atom_structure['creation_time_unix'] = getid3_lib::DateMac2Unix($atom_structure['creation_time']);
             $atom_structure['modify_time_unix'] = getid3_lib::DateMac2Unix($atom_structure['modify_time']);
             $info['quicktime']['time_scale'] = isset($info['quicktime']['time_scale']) ? max($info['quicktime']['time_scale'], $atom_structure['time_scale']) : $atom_structure['time_scale'];
             $info['quicktime']['display_scale'] = $atom_structure['matrix_a'];
             $info['playtime_seconds'] = $atom_structure['duration'] / $atom_structure['time_scale'];
             break;
         case 'tkhd':
             // TracK HeaDer atom
             $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
             $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3));
             $atom_structure['creation_time'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4));
             $atom_structure['modify_time'] = getid3_lib::BigEndian2Int(substr($atom_data, 8, 4));
             $atom_structure['trackid'] = getid3_lib::BigEndian2Int(substr($atom_data, 12, 4));
             $atom_structure['reserved1'] = getid3_lib::BigEndian2Int(substr($atom_data, 16, 4));
             $atom_structure['duration'] = getid3_lib::BigEndian2Int(substr($atom_data, 20, 4));
             $atom_structure['reserved2'] = getid3_lib::BigEndian2Int(substr($atom_data, 24, 8));
             $atom_structure['layer'] = getid3_lib::BigEndian2Int(substr($atom_data, 32, 2));
             $atom_structure['alternate_group'] = getid3_lib::BigEndian2Int(substr($atom_data, 34, 2));
             $atom_structure['volume'] = getid3_lib::FixedPoint8_8(substr($atom_data, 36, 2));
             $atom_structure['reserved3'] = getid3_lib::BigEndian2Int(substr($atom_data, 38, 2));
             // http://developer.apple.com/library/mac/#documentation/QuickTime/RM/MovieBasics/MTEditing/K-Chapter/11MatrixFunctions.html
             // http://developer.apple.com/library/mac/#documentation/QuickTime/qtff/QTFFChap4/qtff4.html#//apple_ref/doc/uid/TP40000939-CH206-18737
             $atom_structure['matrix_a'] = getid3_lib::FixedPoint16_16(substr($atom_data, 40, 4));
             $atom_structure['matrix_b'] = getid3_lib::FixedPoint16_16(substr($atom_data, 44, 4));
             $atom_structure['matrix_u'] = getid3_lib::FixedPoint2_30(substr($atom_data, 48, 4));
             $atom_structure['matrix_c'] = getid3_lib::FixedPoint16_16(substr($atom_data, 52, 4));
             $atom_structure['matrix_d'] = getid3_lib::FixedPoint16_16(substr($atom_data, 56, 4));
             $atom_structure['matrix_v'] = getid3_lib::FixedPoint2_30(substr($atom_data, 60, 4));
             $atom_structure['matrix_x'] = getid3_lib::FixedPoint16_16(substr($atom_data, 64, 4));
             $atom_structure['matrix_y'] = getid3_lib::FixedPoint16_16(substr($atom_data, 68, 4));
             $atom_structure['matrix_w'] = getid3_lib::FixedPoint2_30(substr($atom_data, 72, 4));
             $atom_structure['width'] = getid3_lib::FixedPoint16_16(substr($atom_data, 76, 4));
             $atom_structure['height'] = getid3_lib::FixedPoint16_16(substr($atom_data, 80, 4));
             $atom_structure['flags']['enabled'] = (bool) ($atom_structure['flags_raw'] & 0x1);
             $atom_structure['flags']['in_movie'] = (bool) ($atom_structure['flags_raw'] & 0x2);
             $atom_structure['flags']['in_preview'] = (bool) ($atom_structure['flags_raw'] & 0x4);
             $atom_structure['flags']['in_poster'] = (bool) ($atom_structure['flags_raw'] & 0x8);
             $atom_structure['creation_time_unix'] = getid3_lib::DateMac2Unix($atom_structure['creation_time']);
             $atom_structure['modify_time_unix'] = getid3_lib::DateMac2Unix($atom_structure['modify_time']);
             if ($atom_structure['flags']['enabled'] == 1) {
                 if (!isset($info['video']['resolution_x']) || !isset($info['video']['resolution_y'])) {
                     $info['video']['resolution_x'] = $atom_structure['width'];
                     $info['video']['resolution_y'] = $atom_structure['height'];
                 }
                 $info['video']['resolution_x'] = max($info['video']['resolution_x'], $atom_structure['width']);
                 $info['video']['resolution_y'] = max($info['video']['resolution_y'], $atom_structure['height']);
                 $info['quicktime']['video']['resolution_x'] = $info['video']['resolution_x'];
                 $info['quicktime']['video']['resolution_y'] = $info['video']['resolution_y'];
             } else {
                 // see: http://www.getid3.org/phpBB3/viewtopic.php?t=1295
                 //if (isset($info['video']['resolution_x'])) { unset($info['video']['resolution_x']); }
                 //if (isset($info['video']['resolution_y'])) { unset($info['video']['resolution_y']); }
                 //if (isset($info['quicktime']['video']))    { unset($info['quicktime']['video']);    }
             }
             break;
         case 'iods':
             // Initial Object DeScriptor atom
             // http://www.koders.com/c/fid1FAB3E762903DC482D8A246D4A4BF9F28E049594.aspx?s=windows.h
             // http://libquicktime.sourcearchive.com/documentation/1.0.2plus-pdebian/iods_8c-source.html
             $offset = 0;
             $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, $offset, 1));
             $offset += 1;
             $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, $offset, 3));
             $offset += 3;
             $atom_structure['mp4_iod_tag'] = getid3_lib::BigEndian2Int(substr($atom_data, $offset, 1));
             $offset += 1;
             $atom_structure['length'] = $this->quicktime_read_mp4_descr_length($atom_data, $offset);
             //$offset already adjusted by quicktime_read_mp4_descr_length()
             $atom_structure['object_descriptor_id'] = getid3_lib::BigEndian2Int(substr($atom_data, $offset, 2));
             $offset += 2;
             $atom_structure['od_profile_level'] = getid3_lib::BigEndian2Int(substr($atom_data, $offset, 1));
             $offset += 1;
             $atom_structure['scene_profile_level'] = getid3_lib::BigEndian2Int(substr($atom_data, $offset, 1));
             $offset += 1;
             $atom_structure['audio_profile_id'] = getid3_lib::BigEndian2Int(substr($atom_data, $offset, 1));
             $offset += 1;
             $atom_structure['video_profile_id'] = getid3_lib::BigEndian2Int(substr($atom_data, $offset, 1));
             $offset += 1;
             $atom_structure['graphics_profile_level'] = getid3_lib::BigEndian2Int(substr($atom_data, $offset, 1));
             $offset += 1;
             $atom_structure['num_iods_tracks'] = ($atom_structure['length'] - 7) / 6;
             // 6 bytes would only be right if all tracks use 1-byte length fields
             for ($i = 0; $i < $atom_structure['num_iods_tracks']; $i++) {
                 $atom_structure['track'][$i]['ES_ID_IncTag'] = getid3_lib::BigEndian2Int(substr($atom_data, $offset, 1));
                 $offset += 1;
                 $atom_structure['track'][$i]['length'] = $this->quicktime_read_mp4_descr_length($atom_data, $offset);
                 //$offset already adjusted by quicktime_read_mp4_descr_length()
                 $atom_structure['track'][$i]['track_id'] = getid3_lib::BigEndian2Int(substr($atom_data, $offset, 4));
                 $offset += 4;
             }
             $atom_structure['audio_profile_name'] = $this->QuicktimeIODSaudioProfileName($atom_structure['audio_profile_id']);
             $atom_structure['video_profile_name'] = $this->QuicktimeIODSvideoProfileName($atom_structure['video_profile_id']);
             break;
         case 'ftyp':
             // FileTYPe (?) atom (for MP4 it seems)
             $atom_structure['signature'] = substr($atom_data, 0, 4);
             $atom_structure['unknown_1'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4));
             $atom_structure['fourcc'] = substr($atom_data, 8, 4);
             break;
         case 'mdat':
             // Media DATa atom
             // 'mdat' contains the actual data for the audio/video, possibly also subtitles
             /* due to lack of known documentation, this is a kludge implementation. If you know of documentation on how mdat is properly structed, please send it to info@getid3.org */
             // first, skip any 'wide' padding, and second 'mdat' header (with specified size of zero?)
             $mdat_offset = 0;
             while (true) {
                 if (substr($atom_data, $mdat_offset, 8) == "" . 'wide') {
                     $mdat_offset += 8;
                 } elseif (substr($atom_data, $mdat_offset, 8) == "" . 'mdat') {
                     $mdat_offset += 8;
                 } else {
                     break;
                 }
             }
             // check to see if it looks like chapter titles, in the form of unterminated strings with a leading 16-bit size field
             while (($chapter_string_length = getid3_lib::BigEndian2Int(substr($atom_data, $mdat_offset, 2))) && $chapter_string_length < 1000 && $chapter_string_length <= strlen($atom_data) - $mdat_offset - 2 && preg_match('#^[\\x20-\\xFF]+$#', substr($atom_data, $mdat_offset + 2, $chapter_string_length), $chapter_matches)) {
                 $mdat_offset += 2 + $chapter_string_length;
                 @($info['quicktime']['comments']['chapters'][] = $chapter_matches[0]);
             }
             if ($atomsize > 8 && (!isset($info['avdataend_tmp']) || $info['quicktime'][$atomname]['size'] > $info['avdataend_tmp'] - $info['avdataoffset'])) {
                 $info['avdataoffset'] = $atom_structure['offset'] + 8;
                 // $info['quicktime'][$atomname]['offset'] + 8;
                 $OldAVDataEnd = $info['avdataend'];
                 $info['avdataend'] = $atom_structure['offset'] + $atom_structure['size'];
                 // $info['quicktime'][$atomname]['offset'] + $info['quicktime'][$atomname]['size'];
                 $getid3_temp = new getID3();
                 $getid3_temp->openfile($this->getid3->filename);
                 $getid3_temp->info['avdataoffset'] = $info['avdataoffset'];
                 $getid3_temp->info['avdataend'] = $info['avdataend'];
                 $getid3_mp3 = new getid3_mp3($getid3_temp);
                 if ($getid3_mp3->MPEGaudioHeaderValid($getid3_mp3->MPEGaudioHeaderDecode($this->fread(4)))) {
                     $getid3_mp3->getOnlyMPEGaudioInfo($getid3_temp->info['avdataoffset'], false);
                     if (!empty($getid3_temp->info['warning'])) {
                         foreach ($getid3_temp->info['warning'] as $value) {
                             $info['warning'][] = $value;
                         }
                     }
                     if (!empty($getid3_temp->info['mpeg'])) {
                         $info['mpeg'] = $getid3_temp->info['mpeg'];
                         if (isset($info['mpeg']['audio'])) {
                             $info['audio']['dataformat'] = 'mp3';
                             $info['audio']['codec'] = !empty($info['mpeg']['audio']['encoder']) ? $info['mpeg']['audio']['encoder'] : (!empty($info['mpeg']['audio']['codec']) ? $info['mpeg']['audio']['codec'] : (!empty($info['mpeg']['audio']['LAME']) ? 'LAME' : 'mp3'));
                             $info['audio']['sample_rate'] = $info['mpeg']['audio']['sample_rate'];
                             $info['audio']['channels'] = $info['mpeg']['audio']['channels'];
                             $info['audio']['bitrate'] = $info['mpeg']['audio']['bitrate'];
                             $info['audio']['bitrate_mode'] = strtolower($info['mpeg']['audio']['bitrate_mode']);
                             $info['bitrate'] = $info['audio']['bitrate'];
                         }
                     }
                 }
                 unset($getid3_mp3, $getid3_temp);
                 $info['avdataend'] = $OldAVDataEnd;
                 unset($OldAVDataEnd);
             }
             unset($mdat_offset, $chapter_string_length, $chapter_matches);
             break;
         case 'free':
             // FREE space atom
         // FREE space atom
         case 'skip':
             // SKIP atom
         // SKIP atom
         case 'wide':
             // 64-bit expansion placeholder atom
             // 'free', 'skip' and 'wide' are just padding, contains no useful data at all
             // When writing QuickTime files, it is sometimes necessary to update an atom's size.
             // It is impossible to update a 32-bit atom to a 64-bit atom since the 32-bit atom
             // is only 8 bytes in size, and the 64-bit atom requires 16 bytes. Therefore, QuickTime
             // puts an 8-byte placeholder atom before any atoms it may have to update the size of.
             // In this way, if the atom needs to be converted from a 32-bit to a 64-bit atom, the
             // placeholder atom can be overwritten to obtain the necessary 8 extra bytes.
             // The placeholder atom has a type of kWideAtomPlaceholderType ( 'wide' ).
             break;
         case 'nsav':
             // NoSAVe atom
             // http://developer.apple.com/technotes/tn/tn2038.html
             $atom_structure['data'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 4));
             break;
         case 'ctyp':
             // Controller TYPe atom (seen on QTVR)
             // http://homepages.slingshot.co.nz/~helmboy/quicktime/formats/qtm-layout.txt
             // some controller names are:
             //   0x00 + 'std' for linear movie
             //   'none' for no controls
             $atom_structure['ctyp'] = substr($atom_data, 0, 4);
             $info['quicktime']['controller'] = $atom_structure['ctyp'];
             switch ($atom_structure['ctyp']) {
                 case 'qtvr':
                     $info['video']['dataformat'] = 'quicktimevr';
                     break;
             }
             break;
         case 'pano':
             // PANOrama track (seen on QTVR)
             $atom_structure['pano'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 4));
             break;
         case 'hint':
             // HINT track
         // HINT track
         case 'hinf':
             //
         //
         case 'hinv':
             //
         //
         case 'hnti':
             //
             $info['quicktime']['hinting'] = true;
             break;
         case 'imgt':
             // IMaGe Track reference (kQTVRImageTrackRefType) (seen on QTVR)
             for ($i = 0; $i < $atom_structure['size'] - 8; $i += 4) {
                 $atom_structure['imgt'][] = getid3_lib::BigEndian2Int(substr($atom_data, $i, 4));
             }
             break;
             // Observed-but-not-handled atom types are just listed here to prevent warnings being generated
         // Observed-but-not-handled atom types are just listed here to prevent warnings being generated
         case 'FXTC':
             // Something to do with Adobe After Effects (?)
         // Something to do with Adobe After Effects (?)
         case 'PrmA':
         case 'code':
         case 'FIEL':
             // this is NOT "fiel" (Field Ordering) as describe here: http://developer.apple.com/documentation/QuickTime/QTFF/QTFFChap3/chapter_4_section_2.html
         // this is NOT "fiel" (Field Ordering) as describe here: http://developer.apple.com/documentation/QuickTime/QTFF/QTFFChap3/chapter_4_section_2.html
         case 'tapt':
             // TrackApertureModeDimensionsAID - http://developer.apple.com/documentation/QuickTime/Reference/QT7-1_Update_Reference/Constants/Constants.html
             // tapt seems to be used to compute the video size [http://www.getid3.org/phpBB3/viewtopic.php?t=838]
             // * http://lists.apple.com/archives/quicktime-api/2006/Aug/msg00014.html
             // * http://handbrake.fr/irclogs/handbrake-dev/handbrake-dev20080128_pg2.html
         // TrackApertureModeDimensionsAID - http://developer.apple.com/documentation/QuickTime/Reference/QT7-1_Update_Reference/Constants/Constants.html
         // tapt seems to be used to compute the video size [http://www.getid3.org/phpBB3/viewtopic.php?t=838]
         // * http://lists.apple.com/archives/quicktime-api/2006/Aug/msg00014.html
         // * http://handbrake.fr/irclogs/handbrake-dev/handbrake-dev20080128_pg2.html
         case 'ctts':
             //  STCompositionOffsetAID             - http://developer.apple.com/documentation/QuickTime/Reference/QTRef_Constants/Reference/reference.html
         //  STCompositionOffsetAID             - http://developer.apple.com/documentation/QuickTime/Reference/QTRef_Constants/Reference/reference.html
         case 'cslg':
             //  STCompositionShiftLeastGreatestAID - http://developer.apple.com/documentation/QuickTime/Reference/QTRef_Constants/Reference/reference.html
         //  STCompositionShiftLeastGreatestAID - http://developer.apple.com/documentation/QuickTime/Reference/QTRef_Constants/Reference/reference.html
         case 'sdtp':
             //  STSampleDependencyAID              - http://developer.apple.com/documentation/QuickTime/Reference/QTRef_Constants/Reference/reference.html
         //  STSampleDependencyAID              - http://developer.apple.com/documentation/QuickTime/Reference/QTRef_Constants/Reference/reference.html
         case 'stps':
             //  STPartialSyncSampleAID             - http://developer.apple.com/documentation/QuickTime/Reference/QTRef_Constants/Reference/reference.html
             //$atom_structure['data'] = $atom_data;
             break;
         case "©" . 'xyz':
             // GPS latitude+longitude+altitude
             $atom_structure['data'] = $atom_data;
             if (preg_match('#([\\+\\-][0-9\\.]+)([\\+\\-][0-9\\.]+)([\\+\\-][0-9\\.]+)?/$#i', $atom_data, $matches)) {
                 @(list($all, $latitude, $longitude, $altitude) = $matches);
                 $info['quicktime']['comments']['gps_latitude'][] = floatval($latitude);
                 $info['quicktime']['comments']['gps_longitude'][] = floatval($longitude);
                 if (!empty($altitude)) {
                     $info['quicktime']['comments']['gps_altitude'][] = floatval($altitude);
                 }
             } else {
                 $info['warning'][] = 'QuickTime atom "©xyz" data does not match expected data pattern at offset ' . $baseoffset . '. Please report as getID3() bug.';
             }
             break;
         case 'NCDT':
             // http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Nikon.html
             // Nikon-specific QuickTime tags found in the NCDT atom of MOV videos from some Nikon cameras such as the Coolpix S8000 and D5100
             $atom_structure['subatoms'] = $this->QuicktimeParseContainerAtom($atom_data, $baseoffset + 4, $atomHierarchy, $ParseAllPossibleAtoms);
             break;
         case 'NCTH':
             // Nikon Camera THumbnail image
         // Nikon Camera THumbnail image
         case 'NCVW':
             // Nikon Camera preVieW image
             // http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Nikon.html
             if (preg_match('/^\\xFF\\xD8\\xFF/', $atom_data)) {
                 $atom_structure['data'] = $atom_data;
                 $atom_structure['image_mime'] = 'image/jpeg';
                 $atom_structure['description'] = $atomname == 'NCTH' ? 'Nikon Camera Thumbnail Image' : ($atomname == 'NCVW' ? 'Nikon Camera Preview Image' : 'Nikon preview image');
                 $info['quicktime']['comments']['picture'][] = array('image_mime' => $atom_structure['image_mime'], 'data' => $atom_data, 'description' => $atom_structure['description']);
             }
             break;
         case 'NCTG':
             // Nikon - http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Nikon.html#NCTG
             $atom_structure['data'] = $this->QuicktimeParseNikonNCTG($atom_data);
             break;
         case 'NCHD':
             // Nikon:MakerNoteVersion  - http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Nikon.html
         // Nikon:MakerNoteVersion  - http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Nikon.html
         case 'NCDB':
             // Nikon                   - http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Nikon.html
         // Nikon                   - http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Nikon.html
         case 'CNCV':
             // Canon:CompressorVersion - http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Canon.html
             $atom_structure['data'] = $atom_data;
             break;
         case "":
         case 'meta':
             // METAdata atom
             // some kind of metacontainer, may contain a big data dump such as:
             // mdta keys  mdtacom.apple.quicktime.make (mdtacom.apple.quicktime.creationdate ,mdtacom.apple.quicktime.location.ISO6709 $mdtacom.apple.quicktime.software !mdtacom.apple.quicktime.model ilst   data DEApple 0  (data DE2011-05-11T17:54:04+0200 2  *data DE+52.4936+013.3897+040.247/   data DE4.3.1  data DEiPhone 4
             // http://www.geocities.com/xhelmboyx/quicktime/formats/qti-layout.txt
             $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
             $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3));
             $atom_structure['subatoms'] = $this->QuicktimeParseContainerAtom(substr($atom_data, 4), $baseoffset + 8, $atomHierarchy, $ParseAllPossibleAtoms);
             //$atom_structure['subatoms']  = $this->QuicktimeParseContainerAtom($atom_data, $baseoffset + 8, $atomHierarchy, $ParseAllPossibleAtoms);
             break;
         case 'data':
             // metaDATA atom
             // seems to be 2 bytes language code (ASCII), 2 bytes unknown (set to 0x10B5 in sample I have), remainder is useful data
             $atom_structure['language'] = substr($atom_data, 4 + 0, 2);
             $atom_structure['unknown'] = getid3_lib::BigEndian2Int(substr($atom_data, 4 + 2, 2));
             $atom_structure['data'] = substr($atom_data, 4 + 4);
             break;
         default:
             $info['warning'][] = 'Unknown QuickTime atom type: "' . $atomname . '" (' . trim(getid3_lib::PrintHexBytes($atomname)) . ') at offset ' . $baseoffset;
             $atom_structure['data'] = $atom_data;
             break;
     }
     array_pop($atomHierarchy);
     return $atom_structure;
 }
 function getid3_quicktime(&$fd, &$ThisFileInfo, $ReturnAtomData = true, $ParseAllPossibleAtoms = false)
 {
     $ThisFileInfo['fileformat'] = 'quicktime';
     $ThisFileInfo['quicktime']['hinting'] = false;
     fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
     $offset = 0;
     $atomcounter = 0;
     while ($offset < $ThisFileInfo['avdataend']) {
         if ($offset >= pow(2, 31)) {
             $ThisFileInfo['error'][] = 'Unable to parse atom at offset ' . $offset . ' because beyond 2GB limit of PHP filesystem functions';
             break;
         }
         fseek($fd, $offset, SEEK_SET);
         $AtomHeader = fread($fd, 8);
         $atomsize = getid3_lib::BigEndian2Int(substr($AtomHeader, 0, 4));
         $atomname = substr($AtomHeader, 4, 4);
         // 64-bit MOV patch by jlegate�ktnc*com
         if ($atomsize == 1) {
             $atomsize = getid3_lib::BigEndian2Int(fread($fd, 8));
         }
         $ThisFileInfo['quicktime'][$atomname]['name'] = $atomname;
         $ThisFileInfo['quicktime'][$atomname]['size'] = $atomsize;
         $ThisFileInfo['quicktime'][$atomname]['offset'] = $offset;
         if ($offset + $atomsize > $ThisFileInfo['avdataend']) {
             $ThisFileInfo['error'][] = 'Atom at offset ' . $offset . ' claims to go beyond end-of-file (length: ' . $atomsize . ' bytes)';
             return false;
         }
         if ($atomsize == 0) {
             // Furthermore, for historical reasons the list of atoms is optionally
             // terminated by a 32-bit integer set to 0. If you are writing a program
             // to read user data atoms, you should allow for the terminating 0.
             break;
         }
         switch ($atomname) {
             case 'mdat':
                 // Media DATa atom
                 // 'mdat' contains the actual data for the audio/video
                 if ($atomsize > 8 && (!isset($ThisFileInfo['avdataend_tmp']) || $ThisFileInfo['quicktime'][$atomname]['size'] > $ThisFileInfo['avdataend_tmp'] - $ThisFileInfo['avdataoffset'])) {
                     $ThisFileInfo['avdataoffset'] = $ThisFileInfo['quicktime'][$atomname]['offset'] + 8;
                     $OldAVDataEnd = $ThisFileInfo['avdataend'];
                     $ThisFileInfo['avdataend'] = $ThisFileInfo['quicktime'][$atomname]['offset'] + $ThisFileInfo['quicktime'][$atomname]['size'];
                     if (getid3_mp3::MPEGaudioHeaderValid(getid3_mp3::MPEGaudioHeaderDecode(fread($fd, 4)))) {
                         getid3_mp3::getOnlyMPEGaudioInfo($fd, $ThisFileInfo, $ThisFileInfo['avdataoffset'], false);
                         if (isset($ThisFileInfo['mpeg']['audio'])) {
                             $ThisFileInfo['audio']['dataformat'] = 'mp3';
                             $ThisFileInfo['audio']['codec'] = !empty($ThisFileInfo['mpeg']['audio']['encoder']) ? $ThisFileInfo['mpeg']['audio']['encoder'] : (!empty($ThisFileInfo['mpeg']['audio']['codec']) ? $ThisFileInfo['mpeg']['audio']['codec'] : (!empty($ThisFileInfo['mpeg']['audio']['LAME']) ? 'LAME' : 'mp3'));
                             $ThisFileInfo['audio']['sample_rate'] = $ThisFileInfo['mpeg']['audio']['sample_rate'];
                             $ThisFileInfo['audio']['channels'] = $ThisFileInfo['mpeg']['audio']['channels'];
                             $ThisFileInfo['audio']['bitrate'] = $ThisFileInfo['mpeg']['audio']['bitrate'];
                             $ThisFileInfo['audio']['bitrate_mode'] = strtolower($ThisFileInfo['mpeg']['audio']['bitrate_mode']);
                             $ThisFileInfo['bitrate'] = $ThisFileInfo['audio']['bitrate'];
                         }
                     }
                     $ThisFileInfo['avdataend'] = $OldAVDataEnd;
                     unset($OldAVDataEnd);
                 }
                 break;
             case 'free':
                 // FREE space atom
             // FREE space atom
             case 'skip':
                 // SKIP atom
             // SKIP atom
             case 'wide':
                 // 64-bit expansion placeholder atom
                 // 'free', 'skip' and 'wide' are just padding, contains no useful data at all
                 break;
             default:
                 $atomHierarchy = array();
                 $ThisFileInfo['quicktime'][$atomname] = $this->QuicktimeParseAtom($atomname, $atomsize, fread($fd, $atomsize), $ThisFileInfo, $offset, $atomHierarchy, $ParseAllPossibleAtoms);
                 break;
         }
         $offset += $atomsize;
         $atomcounter++;
     }
     if (!empty($ThisFileInfo['avdataend_tmp'])) {
         // this value is assigned to a temp value and then erased because
         // otherwise any atoms beyond the 'mdat' atom would not get parsed
         $ThisFileInfo['avdataend'] = $ThisFileInfo['avdataend_tmp'];
         unset($ThisFileInfo['avdataend_tmp']);
     }
     if (!isset($ThisFileInfo['bitrate']) && isset($ThisFileInfo['playtime_seconds'])) {
         $ThisFileInfo['bitrate'] = ($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8 / $ThisFileInfo['playtime_seconds'];
     }
     if (isset($ThisFileInfo['bitrate']) && !isset($ThisFileInfo['audio']['bitrate']) && !isset($ThisFileInfo['quicktime']['video'])) {
         $ThisFileInfo['audio']['bitrate'] = $ThisFileInfo['bitrate'];
     }
     if (@$ThisFileInfo['playtime_seconds'] && !isset($ThisFileInfo['video']['frame_rate']) && !empty($ThisFileInfo['quicktime']['stts_framecount'])) {
         foreach ($ThisFileInfo['quicktime']['stts_framecount'] as $key => $samples_count) {
             $samples_per_second = $samples_count / $ThisFileInfo['playtime_seconds'];
             if ($samples_per_second > 240) {
                 // has to be audio samples
             } else {
                 $ThisFileInfo['video']['frame_rate'] = $samples_per_second;
                 break;
             }
         }
     }
     if ($ThisFileInfo['audio']['dataformat'] == 'mp4' && empty($ThisFileInfo['video']['resolution_x'])) {
         $ThisFileInfo['fileformat'] = 'mp4';
         $ThisFileInfo['mime_type'] = 'audio/mp4';
         unset($ThisFileInfo['video']['dataformat']);
     }
     if (!$ReturnAtomData) {
         unset($ThisFileInfo['quicktime']['moov']);
     }
     if (empty($ThisFileInfo['audio']['dataformat']) && !empty($ThisFileInfo['quicktime']['audio'])) {
         $ThisFileInfo['audio']['dataformat'] = 'quicktime';
     }
     if (empty($ThisFileInfo['video']['dataformat']) && !empty($ThisFileInfo['quicktime']['video'])) {
         $ThisFileInfo['video']['dataformat'] = 'quicktime';
     }
     return true;
 }
 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()
 {
     $getid3 = $this->getid3;
     $info =& $getid3->info;
     $getid3->include_module('audio.mp3');
     $info['quicktime'] = array();
     $info_quicktime =& $info['quicktime'];
     $info['fileformat'] = 'quicktime';
     $info_quicktime['hinting'] = false;
     fseek($getid3->fp, $info['avdataoffset'], SEEK_SET);
     $offset = $atom_counter = 0;
     while ($offset < $info['avdataend']) {
         fseek($getid3->fp, $offset, SEEK_SET);
         $atom_header = fread($getid3->fp, 8);
         $atom_size = getid3_lib::BigEndian2Int(substr($atom_header, 0, 4));
         $atom_name = substr($atom_header, 4, 4);
         $info_quicktime[$atom_name]['name'] = $atom_name;
         $info_quicktime[$atom_name]['size'] = $atom_size;
         $info_quicktime[$atom_name]['offset'] = $offset;
         if ($offset + $atom_size > $info['avdataend']) {
             throw new getid3_exception('Atom at offset ' . $offset . ' claims to go beyond end-of-file (length: ' . $atom_size . ' bytes)');
         }
         if ($atom_size == 0) {
             // Furthermore, for historical reasons the list of atoms is optionally
             // terminated by a 32-bit integer set to 0. If you are writing a program
             // to read user data atoms, you should allow for the terminating 0.
             break;
         }
         switch ($atom_name) {
             case 'mdat':
                 // Media DATa atom
                 // 'mdat' contains the actual data for the audio/video
                 if ($atom_size > 8 && (!isset($info['avdataend_tmp']) || $info_quicktime[$atom_name]['size'] > $info['avdataend_tmp'] - $info['avdataoffset'])) {
                     $info['avdataoffset'] = $info_quicktime[$atom_name]['offset'] + 8;
                     $old_av_data_end = $info['avdataend'];
                     $info['avdataend'] = $info_quicktime[$atom_name]['offset'] + $info_quicktime[$atom_name]['size'];
                     //// MP3
                     if (!$getid3->include_module_optional('audio.mp3')) {
                         $getid3->warning('MP3 skipped because mpeg module is missing.');
                     } else {
                         // Clone getid3 - messing with offsets - better safe than sorry
                         $clone = clone $getid3;
                         if (getid3_mp3::MPEGaudioHeaderValid(getid3_mp3::MPEGaudioHeaderDecode(fread($clone->fp, 4)))) {
                             $mp3 = new getid3_mp3($clone);
                             $mp3->AnalyzeMPEGaudioInfo();
                             // Import from clone and destroy
                             if (isset($clone->info['mpeg']['audio'])) {
                                 $info['mpeg']['audio'] = $clone->info['mpeg']['audio'];
                                 $info['audio']['dataformat'] = 'mp3';
                                 $info['audio']['codec'] = !empty($info['mpeg']['audio']['encoder']) ? $info['mpeg']['audio']['encoder'] : (!empty($info['mpeg']['audio']['codec']) ? $info['mpeg']['audio']['codec'] : (!empty($info['mpeg']['audio']['LAME']) ? 'LAME' : 'mp3'));
                                 $info['audio']['sample_rate'] = $info['mpeg']['audio']['sample_rate'];
                                 $info['audio']['channels'] = $info['mpeg']['audio']['channels'];
                                 $info['audio']['bitrate'] = $info['mpeg']['audio']['bitrate'];
                                 $info['audio']['bitrate_mode'] = strtolower($info['mpeg']['audio']['bitrate_mode']);
                                 $info['bitrate'] = $info['audio']['bitrate'];
                                 $getid3->warning($clone->warnings());
                                 unset($clone);
                             }
                         }
                     }
                     $info['avdataend'] = $old_av_data_end;
                     unset($old_av_data_end);
                 }
                 break;
             case 'free':
                 // FREE space atom
             // FREE space atom
             case 'skip':
                 // SKIP atom
             // SKIP atom
             case 'wide':
                 // 64-bit expansion placeholder atom
                 // 'free', 'skip' and 'wide' are just padding, contains no useful data at all
                 break;
             default:
                 $atom_hierarchy = array();
                 $info_quicktime[$atom_name] = $this->QuicktimeParseAtom($atom_name, $atom_size, fread($getid3->fp, $atom_size), $offset, $atom_hierarchy);
                 break;
         }
         $offset += $atom_size;
         $atom_counter++;
     }
     if (!empty($info['avdataend_tmp'])) {
         // this value is assigned to a temp value and then erased because
         // otherwise any atoms beyond the 'mdat' atom would not get parsed
         $info['avdataend'] = $info['avdataend_tmp'];
         unset($info['avdataend_tmp']);
     }
     if (!isset($info['bitrate']) && isset($info['playtime_seconds'])) {
         $info['bitrate'] = ($info['avdataend'] - $info['avdataoffset']) * 8 / $info['playtime_seconds'];
     }
     if (isset($info['bitrate']) && !isset($info['audio']['bitrate']) && !isset($info_quicktime['video'])) {
         $info['audio']['bitrate'] = $info['bitrate'];
     }
     if (@$info['audio']['dataformat'] == 'mp4' && empty($info['video']['resolution_x'])) {
         $info['fileformat'] = 'mp4';
         $info['mime_type'] = 'audio/mp4';
         unset($info['video']['dataformat']);
     }
     if (!$getid3->option_extra_info) {
         unset($info_quicktime['moov']);
     }
     if (empty($info['audio']['dataformat']) && !empty($info_quicktime['audio'])) {
         $info['audio']['dataformat'] = 'quicktime';
     }
     if (empty($info['video']['dataformat']) && !empty($info_quicktime['video'])) {
         $info['video']['dataformat'] = 'quicktime';
     }
     return true;
 }
 public function ParseRIFF($start_offset, $max_offset)
 {
     $getid3 = $this->getid3;
     $info =& $getid3->info;
     $endian_function = $this->endian_function;
     $max_offset = min($max_offset, $info['avdataend']);
     $riff_chunk = false;
     $this->fseek($start_offset, SEEK_SET);
     while ($this->ftell() < $max_offset) {
         $chunk_name = $this->fread(4);
         if (strlen($chunk_name) < 4) {
             throw new getid3_exception('Expecting chunk name at offset ' . ($this->ftell() - 4) . ' but found nothing. Aborting RIFF parsing.');
         }
         $chunk_size = getid3_lib::$endian_function($this->fread(4));
         if ($chunk_size == 0) {
             continue;
             throw new getid3_exception('Chunk size at offset ' . ($this->ftell() - 4) . ' is zero. Aborting RIFF parsing.');
         }
         if ($chunk_size % 2 != 0) {
             // all structures are packed on word boundaries
             $chunk_size++;
         }
         switch ($chunk_name) {
             case 'LIST':
                 $list_name = $this->fread(4);
                 switch ($list_name) {
                     case 'movi':
                     case 'rec ':
                         $riff_chunk[$list_name]['offset'] = $this->ftell() - 4;
                         $riff_chunk[$list_name]['size'] = $chunk_size;
                         static $parsed_audio_stream = false;
                         if (!$parsed_audio_stream) {
                             $where_we_were = $this->ftell();
                             $audio_chunk_header = $this->fread(12);
                             $audio_chunk_stream_num = substr($audio_chunk_header, 0, 2);
                             $audio_chunk_stream_type = substr($audio_chunk_header, 2, 2);
                             $audio_chunk_size = getid3_lib::LittleEndian2Int(substr($audio_chunk_header, 4, 4));
                             if ($audio_chunk_stream_type == 'wb') {
                                 $first_four_bytes = substr($audio_chunk_header, 8, 4);
                                 //// MPEG
                                 if (preg_match('/^\\xFF[\\xE2-\\xE7\\xF2-\\xF7\\xFA-\\xFF][\\x00-\\xEB]/s', $first_four_bytes)) {
                                     if (!$getid3->include_module_optional('audio.mp3')) {
                                         $getid3->warning('MP3 skipped because mp3 module is missing.');
                                     } elseif (getid3_mp3::MPEGaudioHeaderBytesValid($first_four_bytes)) {
                                         // Clone getid3 - messing with offsets - better safe than sorry
                                         $clone = clone $getid3;
                                         $clone->info['avdataoffset'] = $this->ftell() - 4;
                                         $clone->info['avdataend'] = $this->ftell() + $audio_chunk_size;
                                         $mp3 = new getid3_mp3($clone);
                                         $mp3->AnalyzeMPEGaudioInfo();
                                         // Import from clone and destroy
                                         if (isset($clone->info['mpeg']['audio'])) {
                                             $info['mpeg']['audio'] = $clone->info['mpeg']['audio'];
                                             $info['audio']['dataformat'] = 'mp' . $info['mpeg']['audio']['layer'];
                                             $info['audio']['sample_rate'] = $info['mpeg']['audio']['sample_rate'];
                                             $info['audio']['channels'] = $info['mpeg']['audio']['channels'];
                                             $info['audio']['bitrate'] = $info['mpeg']['audio']['bitrate'];
                                             $info['audio']['bitrate_mode'] = strtolower($info['mpeg']['audio']['bitrate_mode']);
                                             $info['bitrate'] = $info['audio']['bitrate'];
                                             $getid3->warning($clone->warnings());
                                             unset($clone);
                                         }
                                     }
                                 } elseif (preg_match('/^\\x0B\\x77/s', $first_four_bytes)) {
                                     if (!$getid3->include_module_optional('audio.ac3')) {
                                         $getid3->warning('AC3 skipped because ac3 module is missing.');
                                     } else {
                                         // Clone getid3 - messing with offsets - better safe than sorry
                                         $clone = clone $getid3;
                                         $clone->info['avdataoffset'] = $this->ftell() - 4;
                                         $clone->info['avdataend'] = $this->ftell() + $audio_chunk_size;
                                         // Analyze clone by fp
                                         $ac3 = new getid3_ac3($clone);
                                         $ac3->Analyze();
                                         // Import from clone and destroy
                                         $info['audio'] = $clone->info['audio'];
                                         $info['ac3'] = $clone->info['ac3'];
                                         $getid3->warning($clone->warnings());
                                         unset($clone);
                                     }
                                 }
                             }
                             $parsed_audio_stream = true;
                             $this->fseek($where_we_were, SEEK_SET);
                         }
                         $this->fseek($chunk_size - 4, SEEK_CUR);
                         break;
                     default:
                         if (!isset($riff_chunk[$list_name])) {
                             $riff_chunk[$list_name] = array();
                         }
                         $list_chunk_parent = $list_name;
                         $list_chunk_max_offset = $this->ftell() - 4 + $chunk_size;
                         if ($parsed_chunk = $this->ParseRIFF($this->ftell(), $this->ftell() + $chunk_size - 4)) {
                             $riff_chunk[$list_name] = array_merge_recursive($riff_chunk[$list_name], $parsed_chunk);
                         }
                         break;
                 }
                 break;
             default:
                 $this_index = 0;
                 if (isset($riff_chunk[$chunk_name]) && is_array($riff_chunk[$chunk_name])) {
                     $this_index = count($riff_chunk[$chunk_name]);
                 }
                 $riff_chunk[$chunk_name][$this_index]['offset'] = $this->ftell() - 8;
                 $riff_chunk[$chunk_name][$this_index]['size'] = $chunk_size;
                 switch ($chunk_name) {
                     case 'data':
                         $info['avdataoffset'] = $this->ftell();
                         $info['avdataend'] = $info['avdataoffset'] + $chunk_size;
                         $riff_data_chunk_contents_test = $this->fread(36);
                         //// This is probably MP3 data
                         if (strlen($riff_data_chunk_contents_test) > 0 && preg_match('/^\\xFF[\\xE2-\\xE7\\xF2-\\xF7\\xFA-\\xFF][\\x00-\\xEB]/s', substr($riff_data_chunk_contents_test, 0, 4))) {
                             try {
                                 if (!$getid3->include_module_optional('audio.mp3')) {
                                     $getid3->warning('MP3 skipped because mp3 module is missing.');
                                 }
                                 // Clone getid3 - messing with offsets - better safe than sorry
                                 $clone = clone $getid3;
                                 if (getid3_mp3::MPEGaudioHeaderBytesValid(substr($riff_data_chunk_contents_test, 0, 4))) {
                                     $mp3 = new getid3_mp3($clone);
                                     $mp3->AnalyzeMPEGaudioInfo();
                                     // Import from clone and destroy
                                     if (isset($clone->info['mpeg']['audio'])) {
                                         $info['mpeg']['audio'] = $clone->info['mpeg']['audio'];
                                         $info['audio']['sample_rate'] = $info['mpeg']['audio']['sample_rate'];
                                         $info['audio']['channels'] = $info['mpeg']['audio']['channels'];
                                         $info['audio']['bitrate'] = $info['mpeg']['audio']['bitrate'];
                                         $info['audio']['bitrate_mode'] = strtolower($info['mpeg']['audio']['bitrate_mode']);
                                         $info['bitrate'] = $info['audio']['bitrate'];
                                         $getid3->warning($clone->warnings());
                                         unset($clone);
                                     }
                                 }
                             } catch (Exception $e) {
                                 // do nothing - not MP3 data
                             }
                         } elseif (strlen($riff_data_chunk_contents_test) > 0 && substr($riff_data_chunk_contents_test, 0, 2) == "\vw") {
                             if (!$getid3->include_module_optional('audio.ac3')) {
                                 $getid3->warning('AC3 skipped because ac3 module is missing.');
                             } else {
                                 // Clone getid3 - messing with offsets - better safe than sorry
                                 $clone = clone $getid3;
                                 $clone->info['avdataoffset'] = $riff_chunk[$chunk_name][$this_index]['offset'];
                                 $clone->info['avdataend'] = $clone->info['avdataoffset'] + $riff_chunk[$chunk_name][$this_index]['size'];
                                 // Analyze clone by fp
                                 $ac3 = new getid3_ac3($clone);
                                 $ac3->Analyze();
                                 // Import from clone and destroy
                                 $info['audio'] = $clone->info['audio'];
                                 $info['ac3'] = $clone->info['ac3'];
                                 $getid3->warning($clone->warnings());
                                 unset($clone);
                             }
                         } elseif (strlen($riff_data_chunk_contents_test) > 0 && substr($riff_data_chunk_contents_test, 8, 2) == "w\v") {
                             if (!$getid3->include_module_optional('audio.ac3')) {
                                 $getid3->warning('AC3 skipped because ac3 module is missing.');
                             } else {
                                 // Extract ac3 data to string
                                 $ac3_data = '';
                                 for ($i = 0; $i < 28; $i += 2) {
                                     // swap byte order
                                     $ac3_data .= substr($riff_data_chunk_contents_test, 8 + $i + 1, 1);
                                     $ac3_data .= substr($riff_data_chunk_contents_test, 8 + $i + 0, 1);
                                 }
                                 // Clone getid3 - messing with offsets - better safe than sorry
                                 $clone = clone $getid3;
                                 $clone->info['avdataoffset'] = 0;
                                 $clone->info['avdataend'] = 20;
                                 // Analyse clone by string
                                 $ac3 = new getid3_ac3($clone);
                                 $ac3->AnalyzeString($ac3_data);
                                 // Import from clone and destroy
                                 $info['audio'] = $clone->info['audio'];
                                 $info['ac3'] = $clone->info['ac3'];
                                 $getid3->warning($clone->warnings());
                                 unset($clone);
                             }
                         }
                         if (strlen($riff_data_chunk_contents_test) > 0 && substr($riff_data_chunk_contents_test, 0, 4) == 'wvpk') {
                             // This is WavPack data
                             $info['wavpack']['offset'] = $riff_chunk[$chunk_name][$this_index]['offset'];
                             $info['wavpack']['size'] = getid3_lib::LittleEndian2Int(substr($riff_data_chunk_contents_test, 4, 4));
                             $this->RIFFparseWavPackHeader(substr($riff_data_chunk_contents_test, 8, 28));
                         } else {
                             // This is some other kind of data (quite possibly just PCM)
                             // do nothing special, just skip it
                         }
                         $this->fseek($riff_chunk[$chunk_name][$this_index]['offset'] + 8 + $chunk_size, SEEK_SET);
                         break;
                     case 'bext':
                     case 'cart':
                     case 'fmt ':
                     case 'MEXT':
                     case 'DISP':
                         // always read data in
                         $riff_chunk[$chunk_name][$this_index]['data'] = $this->fread($chunk_size);
                         break;
                     default:
                         if (!empty($list_chunk_parent) && $riff_chunk[$chunk_name][$this_index]['offset'] + $riff_chunk[$chunk_name][$this_index]['size'] <= $list_chunk_max_offset) {
                             $riff_chunk[$list_chunk_parent][$chunk_name][$this_index]['offset'] = $riff_chunk[$chunk_name][$this_index]['offset'];
                             $riff_chunk[$list_chunk_parent][$chunk_name][$this_index]['size'] = $riff_chunk[$chunk_name][$this_index]['size'];
                             unset($riff_chunk[$chunk_name][$this_index]['offset']);
                             unset($riff_chunk[$chunk_name][$this_index]['size']);
                             if (isset($riff_chunk[$chunk_name][$this_index]) && empty($riff_chunk[$chunk_name][$this_index])) {
                                 unset($riff_chunk[$chunk_name][$this_index]);
                             }
                             if (isset($riff_chunk[$chunk_name]) && empty($riff_chunk[$chunk_name])) {
                                 unset($riff_chunk[$chunk_name]);
                             }
                             $riff_chunk[$list_chunk_parent][$chunk_name][$this_index]['data'] = $this->fread($chunk_size);
                         } elseif ($chunk_size < 2048) {
                             // only read data in if smaller than 2kB
                             $riff_chunk[$chunk_name][$this_index]['data'] = $this->fread($chunk_size);
                         } else {
                             $this->fseek($chunk_size, SEEK_CUR);
                         }
                         break;
                 }
                 break;
         }
     }
     return $riff_chunk;
 }
 function ParseRIFF($startoffset, $maxoffset)
 {
     $info =& $this->getid3->info;
     $maxoffset = min($maxoffset, $info['avdataend']);
     $RIFFchunk = false;
     $FoundAllChunksWeNeed = false;
     if ($startoffset < 0 || !getid3_lib::intValueSupported($startoffset)) {
         $info['warning'][] = 'Unable to ParseRIFF() at ' . $startoffset . ' because beyond ' . round(PHP_INT_MAX / 1073741824) . 'GB limit of PHP filesystem functions';
         return false;
     }
     $max_usable_offset = min(PHP_INT_MAX - 1024, $maxoffset);
     if ($maxoffset > $max_usable_offset) {
         $info['warning'][] = 'ParseRIFF() may return incomplete data for chunk starting at ' . $startoffset . ' because beyond it extends to ' . $maxoffset . ', which is beyond the ' . round(PHP_INT_MAX / 1073741824) . 'GB limit of PHP filesystem functions';
     }
     fseek($this->getid3->fp, $startoffset, SEEK_SET);
     while (ftell($this->getid3->fp) < $max_usable_offset) {
         $chunknamesize = fread($this->getid3->fp, 8);
         $chunkname = substr($chunknamesize, 0, 4);
         $chunksize = $this->EitherEndian2Int(substr($chunknamesize, 4, 4));
         if (strlen($chunkname) < 4) {
             $info['error'][] = 'Expecting chunk name at offset ' . (ftell($this->getid3->fp) - 4) . ' but found nothing. Aborting RIFF parsing.';
             break;
         }
         if ($chunksize == 0) {
             if ($chunkname == 'JUNK') {
                 // we'll allow zero-size JUNK frames
             } else {
                 $info['warning'][] = 'Chunk size at offset ' . (ftell($this->getid3->fp) - 4) . ' is zero. Aborting RIFF parsing.';
                 break;
             }
         }
         if ($chunksize % 2 != 0) {
             // all structures are packed on word boundaries
             $chunksize++;
         }
         switch ($chunkname) {
             case 'LIST':
                 $listname = fread($this->getid3->fp, 4);
                 if (preg_match('#^(movi|rec )$#i', $listname)) {
                     $RIFFchunk[$listname]['offset'] = ftell($this->getid3->fp) - 4;
                     $RIFFchunk[$listname]['size'] = $chunksize;
                     if ($FoundAllChunksWeNeed) {
                         // skip over
                     } else {
                         $WhereWeWere = ftell($this->getid3->fp);
                         $AudioChunkHeader = fread($this->getid3->fp, 12);
                         $AudioChunkStreamNum = substr($AudioChunkHeader, 0, 2);
                         $AudioChunkStreamType = substr($AudioChunkHeader, 2, 2);
                         $AudioChunkSize = getid3_lib::LittleEndian2Int(substr($AudioChunkHeader, 4, 4));
                         if ($AudioChunkStreamType == 'wb') {
                             $FirstFourBytes = substr($AudioChunkHeader, 8, 4);
                             if (preg_match('/^\\xFF[\\xE2-\\xE7\\xF2-\\xF7\\xFA-\\xFF][\\x00-\\xEB]/s', $FirstFourBytes)) {
                                 // MP3
                                 if (getid3_mp3::MPEGaudioHeaderBytesValid($FirstFourBytes)) {
                                     $getid3_temp = new getID3();
                                     $getid3_temp->openfile($this->getid3->filename);
                                     $getid3_temp->info['avdataoffset'] = ftell($this->getid3->fp) - 4;
                                     $getid3_temp->info['avdataend'] = ftell($this->getid3->fp) + $AudioChunkSize;
                                     $getid3_mp3 = new getid3_mp3($getid3_temp);
                                     $getid3_mp3->getOnlyMPEGaudioInfo($getid3_temp->info['avdataoffset'], false);
                                     if (isset($getid3_temp->info['mpeg']['audio'])) {
                                         $info['mpeg']['audio'] = $getid3_temp->info['mpeg']['audio'];
                                         $info['audio'] = $getid3_temp->info['audio'];
                                         $info['audio']['dataformat'] = 'mp' . $info['mpeg']['audio']['layer'];
                                         $info['audio']['sample_rate'] = $info['mpeg']['audio']['sample_rate'];
                                         $info['audio']['channels'] = $info['mpeg']['audio']['channels'];
                                         $info['audio']['bitrate'] = $info['mpeg']['audio']['bitrate'];
                                         $info['audio']['bitrate_mode'] = strtolower($info['mpeg']['audio']['bitrate_mode']);
                                         //$info['bitrate']               = $info['audio']['bitrate'];
                                     }
                                     unset($getid3_temp, $getid3_mp3);
                                 }
                             } elseif (preg_match('/^\\x0B\\x77/s', $FirstFourBytes)) {
                                 // AC3
                                 if (getid3_lib::IncludeDependency(GETID3_INCLUDEPATH . 'module.audio.ac3.php', __FILE__, false)) {
                                     $getid3_temp = new getID3();
                                     $getid3_temp->openfile($this->getid3->filename);
                                     $getid3_temp->info['avdataoffset'] = ftell($this->getid3->fp) - 4;
                                     $getid3_temp->info['avdataend'] = ftell($this->getid3->fp) + $AudioChunkSize;
                                     $getid3_ac3 = new getid3_ac3($getid3_temp);
                                     $getid3_ac3->Analyze();
                                     if (empty($getid3_temp->info['error'])) {
                                         $info['audio'] = $getid3_temp->info['audio'];
                                         $info['ac3'] = $getid3_temp->info['ac3'];
                                         if (!empty($getid3_temp->info['warning'])) {
                                             foreach ($getid3_temp->info['warning'] as $key => $value) {
                                                 $info['warning'][] = $value;
                                             }
                                         }
                                     }
                                     unset($getid3_temp, $getid3_ac3);
                                 }
                             }
                         }
                         $FoundAllChunksWeNeed = true;
                         fseek($this->getid3->fp, $WhereWeWere, SEEK_SET);
                     }
                     fseek($this->getid3->fp, $chunksize - 4, SEEK_CUR);
                     //} elseif (preg_match('#^[0-9]{2}(wb|pc|dc|db)$#i', $listname)) {
                     //
                     //	// data chunk, ignore
                     //
                 } else {
                     if (!isset($RIFFchunk[$listname])) {
                         $RIFFchunk[$listname] = array();
                     }
                     $LISTchunkParent = $listname;
                     $LISTchunkMaxOffset = ftell($this->getid3->fp) - 4 + $chunksize;
                     if ($parsedChunk = $this->ParseRIFF(ftell($this->getid3->fp), ftell($this->getid3->fp) + $chunksize - 4)) {
                         $RIFFchunk[$listname] = array_merge_recursive($RIFFchunk[$listname], $parsedChunk);
                     }
                 }
                 break;
             default:
                 if (preg_match('#^[0-9]{2}(wb|pc|dc|db)$#', $chunkname)) {
                     $nextoffset = ftell($this->getid3->fp) + $chunksize;
                     if ($nextoffset < 0 || !getid3_lib::intValueSupported($nextoffset)) {
                         $info['warning'][] = 'Unable to parse chunk at offset ' . $nextoffset . ' because beyond ' . round(PHP_INT_MAX / 1073741824) . 'GB limit of PHP filesystem functions';
                         break 2;
                     }
                     fseek($this->getid3->fp, $nextoffset, SEEK_SET);
                     break;
                 }
                 $thisindex = 0;
                 if (isset($RIFFchunk[$chunkname]) && is_array($RIFFchunk[$chunkname])) {
                     $thisindex = count($RIFFchunk[$chunkname]);
                 }
                 $RIFFchunk[$chunkname][$thisindex]['offset'] = ftell($this->getid3->fp) - 8;
                 $RIFFchunk[$chunkname][$thisindex]['size'] = $chunksize;
                 switch ($chunkname) {
                     case 'data':
                         $info['avdataoffset'] = ftell($this->getid3->fp);
                         $info['avdataend'] = $info['avdataoffset'] + $chunksize;
                         $RIFFdataChunkContentsTest = fread($this->getid3->fp, 36);
                         if (strlen($RIFFdataChunkContentsTest) > 0 && preg_match('/^\\xFF[\\xE2-\\xE7\\xF2-\\xF7\\xFA-\\xFF][\\x00-\\xEB]/s', substr($RIFFdataChunkContentsTest, 0, 4))) {
                             // Probably is MP3 data
                             if (getid3_mp3::MPEGaudioHeaderBytesValid(substr($RIFFdataChunkContentsTest, 0, 4))) {
                                 $getid3_temp = new getID3();
                                 $getid3_temp->openfile($this->getid3->filename);
                                 $getid3_temp->info['avdataoffset'] = $RIFFchunk[$chunkname][$thisindex]['offset'];
                                 $getid3_temp->info['avdataend'] = $RIFFchunk[$chunkname][$thisindex]['offset'] + $RIFFchunk[$chunkname][$thisindex]['size'];
                                 $getid3_mp3 = new getid3_mp3($getid3_temp);
                                 $getid3_mp3->getOnlyMPEGaudioInfo($RIFFchunk[$chunkname][$thisindex]['offset'], false);
                                 if (empty($getid3_temp->info['error'])) {
                                     $info['mpeg'] = $getid3_temp->info['mpeg'];
                                     $info['audio'] = $getid3_temp->info['audio'];
                                 }
                                 unset($getid3_temp, $getid3_mp3);
                             }
                         } elseif (strlen($RIFFdataChunkContentsTest) > 0 && substr($RIFFdataChunkContentsTest, 0, 2) == "\vw") {
                             // This is probably AC-3 data
                             if (getid3_lib::IncludeDependency(GETID3_INCLUDEPATH . 'module.audio.ac3.php', __FILE__, false)) {
                                 $getid3_temp = new getID3();
                                 $getid3_temp->openfile($this->getid3->filename);
                                 $getid3_temp->info['avdataoffset'] = $RIFFchunk[$chunkname][$thisindex]['offset'];
                                 $getid3_temp->info['avdataend'] = $RIFFchunk[$chunkname][$thisindex]['offset'] + $RIFFchunk[$chunkname][$thisindex]['size'];
                                 $getid3_ac3 = new getid3_ac3($getid3_temp);
                                 $getid3_ac3->Analyze();
                                 if (empty($getid3_temp->info['error'])) {
                                     $info['audio'] = $getid3_temp->info['audio'];
                                     $info['ac3'] = $getid3_temp->info['ac3'];
                                     $info['warning'] = $getid3_temp->info['warning'];
                                 }
                                 unset($getid3_temp, $getid3_ac3);
                             }
                         } elseif (strlen($RIFFdataChunkContentsTest) > 0 && substr($RIFFdataChunkContentsTest, 8, 2) == "w\v") {
                             // Dolby Digital WAV
                             // AC-3 content, but not encoded in same format as normal AC-3 file
                             // For one thing, byte order is swapped
                             if (getid3_lib::IncludeDependency(GETID3_INCLUDEPATH . 'module.audio.ac3.php', __FILE__, false)) {
                                 // ok to use tmpfile here - only 56 bytes
                                 if ($RIFFtempfilename = tempnam(GETID3_TEMP_DIR, 'id3')) {
                                     if ($fd_temp = fopen($RIFFtempfilename, 'wb')) {
                                         for ($i = 0; $i < 28; $i += 2) {
                                             // swap byte order
                                             fwrite($fd_temp, substr($RIFFdataChunkContentsTest, 8 + $i + 1, 1));
                                             fwrite($fd_temp, substr($RIFFdataChunkContentsTest, 8 + $i + 0, 1));
                                         }
                                         fclose($fd_temp);
                                         $getid3_temp = new getID3();
                                         $getid3_temp->openfile($RIFFtempfilename);
                                         $getid3_temp->info['avdataend'] = 20;
                                         $getid3_ac3 = new getid3_ac3($getid3_temp);
                                         $getid3_ac3->Analyze();
                                         if (empty($getid3_temp->info['error'])) {
                                             $info['audio'] = $getid3_temp->info['audio'];
                                             $info['ac3'] = $getid3_temp->info['ac3'];
                                             $info['warning'] = $getid3_temp->info['warning'];
                                         } else {
                                             $info['error'][] = 'Error parsing Dolby Digital WAV (AC3-in-RIFF): ' . implode(';', $getid3_temp->info['error']);
                                         }
                                         unset($getid3_ac3, $getid3_temp);
                                     } else {
                                         $info['error'][] = 'Error parsing Dolby Digital WAV (AC3-in-RIFF): failed to write temp file';
                                     }
                                     unlink($RIFFtempfilename);
                                 } else {
                                     $info['error'][] = 'Error parsing Dolby Digital WAV (AC3-in-RIFF): failed to write temp file';
                                 }
                             }
                         } elseif (strlen($RIFFdataChunkContentsTest) > 0 && substr($RIFFdataChunkContentsTest, 0, 4) == 'wvpk') {
                             // This is WavPack data
                             $info['wavpack']['offset'] = $RIFFchunk[$chunkname][$thisindex]['offset'];
                             $info['wavpack']['size'] = getid3_lib::LittleEndian2Int(substr($RIFFdataChunkContentsTest, 4, 4));
                             $this->RIFFparseWavPackHeader(substr($RIFFdataChunkContentsTest, 8, 28));
                         } else {
                             // This is some other kind of data (quite possibly just PCM)
                             // do nothing special, just skip it
                         }
                         $nextoffset = $RIFFchunk[$chunkname][$thisindex]['offset'] + 8 + $chunksize;
                         if ($nextoffset < 0 || !getid3_lib::intValueSupported($nextoffset)) {
                             $info['warning'][] = 'Unable to parse chunk at offset ' . $nextoffset . ' because beyond ' . round(PHP_INT_MAX / 1073741824) . 'GB limit of PHP filesystem functions';
                             break 3;
                         }
                         fseek($this->getid3->fp, $RIFFchunk[$chunkname][$thisindex]['offset'] + 8 + $chunksize, SEEK_SET);
                         break;
                     case 'iXML':
                     case 'bext':
                     case 'cart':
                     case 'fmt ':
                     case 'strh':
                     case 'strf':
                     case 'indx':
                     case 'MEXT':
                     case 'DISP':
                         // always read data in
                     // always read data in
                     case 'JUNK':
                         // should be: never read data in
                         // but some programs write their version strings in a JUNK chunk (e.g. VirtualDub, AVIdemux, etc)
                         if ($chunksize < 1048576) {
                             if ($chunksize > 0) {
                                 $RIFFchunk[$chunkname][$thisindex]['data'] = fread($this->getid3->fp, $chunksize);
                                 if ($chunkname == 'JUNK') {
                                     if (preg_match('#^([\\x20-\\x7F]+)#', $RIFFchunk[$chunkname][$thisindex]['data'], $matches)) {
                                         // only keep text characters [chr(32)-chr(127)]
                                         $info['riff']['comments']['junk'][] = trim($matches[1]);
                                     }
                                     // but if nothing there, ignore
                                     // remove the key in either case
                                     unset($RIFFchunk[$chunkname][$thisindex]['data']);
                                 }
                             }
                         } else {
                             $info['warning'][] = 'chunk "' . $chunkname . '" at offset ' . ftell($this->getid3->fp) . ' is unexpectedly larger than 1MB (claims to be ' . number_format($chunksize) . ' bytes), skipping data';
                             $nextoffset = ftell($this->getid3->fp) + $chunksize;
                             if ($nextoffset < 0 || !getid3_lib::intValueSupported($nextoffset)) {
                                 $info['warning'][] = 'Unable to parse chunk at offset ' . $nextoffset . ' because beyond ' . round(PHP_INT_MAX / 1073741824) . 'GB limit of PHP filesystem functions';
                                 break 3;
                             }
                             fseek($this->getid3->fp, $nextoffset, SEEK_SET);
                         }
                         break;
                     default:
                         if (!preg_match('#^[0-9]{2}(wb|pc|dc|db)$#', $chunkname) && !empty($LISTchunkParent) && $RIFFchunk[$chunkname][$thisindex]['offset'] + $RIFFchunk[$chunkname][$thisindex]['size'] <= $LISTchunkMaxOffset) {
                             $RIFFchunk[$LISTchunkParent][$chunkname][$thisindex]['offset'] = $RIFFchunk[$chunkname][$thisindex]['offset'];
                             $RIFFchunk[$LISTchunkParent][$chunkname][$thisindex]['size'] = $RIFFchunk[$chunkname][$thisindex]['size'];
                             unset($RIFFchunk[$chunkname][$thisindex]['offset']);
                             unset($RIFFchunk[$chunkname][$thisindex]['size']);
                             if (isset($RIFFchunk[$chunkname][$thisindex]) && empty($RIFFchunk[$chunkname][$thisindex])) {
                                 unset($RIFFchunk[$chunkname][$thisindex]);
                             }
                             if (isset($RIFFchunk[$chunkname]) && empty($RIFFchunk[$chunkname])) {
                                 unset($RIFFchunk[$chunkname]);
                             }
                             $RIFFchunk[$LISTchunkParent][$chunkname][$thisindex]['data'] = fread($this->getid3->fp, $chunksize);
                             //} elseif (in_array($chunkname, array('ID3 ')) || (($chunksize > 0) && ($chunksize < 2048))) {
                         } elseif ($chunksize > 0 && $chunksize < 2048) {
                             // only read data in if smaller than 2kB
                             $RIFFchunk[$chunkname][$thisindex]['data'] = fread($this->getid3->fp, $chunksize);
                         } else {
                             $nextoffset = ftell($this->getid3->fp) + $chunksize;
                             if ($nextoffset < 0 || !getid3_lib::intValueSupported($nextoffset)) {
                                 $info['warning'][] = 'Unable to parse chunk at offset ' . $nextoffset . ' because beyond ' . round(PHP_INT_MAX / 1073741824) . 'GB limit of PHP filesystem functions';
                                 break 3;
                             }
                             fseek($this->getid3->fp, $nextoffset, SEEK_SET);
                         }
                         break;
                 }
                 break;
         }
     }
     return $RIFFchunk;
 }
 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;
 }
 function Analyze()
 {
     $info =& $this->getid3->info;
     $info['fileformat'] = 'quicktime';
     $info['quicktime']['hinting'] = false;
     $info['quicktime']['controller'] = 'standard';
     // may be overridden if 'ctyp' atom is present
     fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET);
     $offset = 0;
     $atomcounter = 0;
     while ($offset < $info['avdataend']) {
         if (!getid3_lib::intValueSupported($offset)) {
             $info['error'][] = 'Unable to parse atom at offset ' . $offset . ' because beyond ' . round(PHP_INT_MAX / 1073741824) . 'GB limit of PHP filesystem functions';
             break;
         }
         fseek($this->getid3->fp, $offset, SEEK_SET);
         $AtomHeader = fread($this->getid3->fp, 8);
         $atomsize = getid3_lib::BigEndian2Int(substr($AtomHeader, 0, 4));
         $atomname = substr($AtomHeader, 4, 4);
         // 64-bit MOV patch by jlegateØktnc*com
         if ($atomsize == 1) {
             $atomsize = getid3_lib::BigEndian2Int(fread($this->getid3->fp, 8));
         }
         $info['quicktime'][$atomname]['name'] = $atomname;
         $info['quicktime'][$atomname]['size'] = $atomsize;
         $info['quicktime'][$atomname]['offset'] = $offset;
         if ($offset + $atomsize > $info['avdataend']) {
             $info['error'][] = 'Atom at offset ' . $offset . ' claims to go beyond end-of-file (length: ' . $atomsize . ' bytes)';
             return false;
         }
         if ($atomsize == 0) {
             // Furthermore, for historical reasons the list of atoms is optionally
             // terminated by a 32-bit integer set to 0. If you are writing a program
             // to read user data atoms, you should allow for the terminating 0.
             break;
         }
         switch ($atomname) {
             case 'mdat':
                 // Media DATa atom
                 // 'mdat' contains the actual data for the audio/video
                 if ($atomsize > 8 && (!isset($info['avdataend_tmp']) || $info['quicktime'][$atomname]['size'] > $info['avdataend_tmp'] - $info['avdataoffset'])) {
                     $info['avdataoffset'] = $info['quicktime'][$atomname]['offset'] + 8;
                     $OldAVDataEnd = $info['avdataend'];
                     $info['avdataend'] = $info['quicktime'][$atomname]['offset'] + $info['quicktime'][$atomname]['size'];
                     $getid3_temp = new getID3();
                     $getid3_temp->openfile($this->getid3->filename);
                     $getid3_temp->info['avdataoffset'] = $info['avdataoffset'];
                     $getid3_temp->info['avdataend'] = $info['avdataend'];
                     $getid3_mp3 = new getid3_mp3($getid3_temp);
                     if ($getid3_mp3->MPEGaudioHeaderValid($getid3_mp3->MPEGaudioHeaderDecode(fread($this->getid3->fp, 4)))) {
                         $getid3_mp3->getOnlyMPEGaudioInfo($getid3_temp->info['avdataoffset'], false);
                         if (!empty($getid3_temp->info['warning'])) {
                             foreach ($getid3_temp->info['warning'] as $value) {
                                 $info['warning'][] = $value;
                             }
                         }
                         if (!empty($getid3_temp->info['mpeg'])) {
                             $info['mpeg'] = $getid3_temp->info['mpeg'];
                             if (isset($info['mpeg']['audio'])) {
                                 $info['audio']['dataformat'] = 'mp3';
                                 $info['audio']['codec'] = !empty($info['mpeg']['audio']['encoder']) ? $info['mpeg']['audio']['encoder'] : (!empty($info['mpeg']['audio']['codec']) ? $info['mpeg']['audio']['codec'] : (!empty($info['mpeg']['audio']['LAME']) ? 'LAME' : 'mp3'));
                                 $info['audio']['sample_rate'] = $info['mpeg']['audio']['sample_rate'];
                                 $info['audio']['channels'] = $info['mpeg']['audio']['channels'];
                                 $info['audio']['bitrate'] = $info['mpeg']['audio']['bitrate'];
                                 $info['audio']['bitrate_mode'] = strtolower($info['mpeg']['audio']['bitrate_mode']);
                                 $info['bitrate'] = $info['audio']['bitrate'];
                             }
                         }
                     }
                     unset($getid3_mp3, $getid3_temp);
                     $info['avdataend'] = $OldAVDataEnd;
                     unset($OldAVDataEnd);
                 }
                 break;
             case 'free':
                 // FREE space atom
             // FREE space atom
             case 'skip':
                 // SKIP atom
             // SKIP atom
             case 'wide':
                 // 64-bit expansion placeholder atom
                 // 'free', 'skip' and 'wide' are just padding, contains no useful data at all
                 break;
             default:
                 $atomHierarchy = array();
                 $info['quicktime'][$atomname] = $this->QuicktimeParseAtom($atomname, $atomsize, fread($this->getid3->fp, $atomsize), $offset, $atomHierarchy, $this->ParseAllPossibleAtoms);
                 break;
         }
         $offset += $atomsize;
         $atomcounter++;
     }
     if (!empty($info['avdataend_tmp'])) {
         // this value is assigned to a temp value and then erased because
         // otherwise any atoms beyond the 'mdat' atom would not get parsed
         $info['avdataend'] = $info['avdataend_tmp'];
         unset($info['avdataend_tmp']);
     }
     if (!isset($info['bitrate']) && isset($info['playtime_seconds'])) {
         $info['bitrate'] = ($info['avdataend'] - $info['avdataoffset']) * 8 / $info['playtime_seconds'];
     }
     if (isset($info['bitrate']) && !isset($info['audio']['bitrate']) && !isset($info['quicktime']['video'])) {
         $info['audio']['bitrate'] = $info['bitrate'];
     }
     if (!empty($info['playtime_seconds']) && !isset($info['video']['frame_rate']) && !empty($info['quicktime']['stts_framecount'])) {
         foreach ($info['quicktime']['stts_framecount'] as $key => $samples_count) {
             $samples_per_second = $samples_count / $info['playtime_seconds'];
             if ($samples_per_second > 240) {
                 // has to be audio samples
             } else {
                 $info['video']['frame_rate'] = $samples_per_second;
                 break;
             }
         }
     }
     if ($info['audio']['dataformat'] == 'mp4' && empty($info['video']['resolution_x'])) {
         $info['fileformat'] = 'mp4';
         $info['mime_type'] = 'audio/mp4';
         unset($info['video']['dataformat']);
     }
     if (!$this->ReturnAtomData) {
         unset($info['quicktime']['moov']);
     }
     if (empty($info['audio']['dataformat']) && !empty($info['quicktime']['audio'])) {
         $info['audio']['dataformat'] = 'quicktime';
     }
     if (empty($info['video']['dataformat']) && !empty($info['quicktime']['video'])) {
         $info['video']['dataformat'] = 'quicktime';
     }
     return true;
 }
Exemplo n.º 15
0
 static function ParseRIFF(&$fd, $startoffset, $maxoffset, &$ThisFileInfo)
 {
     $maxoffset = min($maxoffset, $ThisFileInfo['avdataend']);
     $RIFFchunk = false;
     $FoundAllChunksWeNeed = false;
     if ($startoffset < 0 || !getid3_lib::intValueSupported($startoffset)) {
         $ThisFileInfo['warning'][] = 'Unable to ParseRIFF() at ' . $startoffset . ' because beyond ' . round(PHP_INT_MAX / 1073741824) . 'GB limit of PHP filesystem functions';
         return false;
     }
     $max_usable_offset = min(PHP_INT_MAX - 1024, $maxoffset);
     if ($maxoffset > $max_usable_offset) {
         $ThisFileInfo['warning'][] = 'ParseRIFF() may return incomplete data for chunk starting at ' . $startoffset . ' because beyond it extends to ' . $maxoffset . ', which is beyond the ' . round(PHP_INT_MAX / 1073741824) . 'GB limit of PHP filesystem functions';
     }
     fseek($fd, $startoffset, SEEK_SET);
     while (ftell($fd) < $max_usable_offset) {
         $chunknamesize = fread($fd, 8);
         $chunkname = substr($chunknamesize, 0, 4);
         $chunksize = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($chunknamesize, 4, 4));
         if (strlen($chunkname) < 4) {
             $ThisFileInfo['error'][] = 'Expecting chunk name at offset ' . (ftell($fd) - 4) . ' but found nothing. Aborting RIFF parsing.';
             break;
         }
         if ($chunksize == 0) {
             if ($chunkname == 'JUNK') {
                 // we'll allow zero-size JUNK frames
             } else {
                 $ThisFileInfo['warning'][] = 'Chunk size at offset ' . (ftell($fd) - 4) . ' is zero. Aborting RIFF parsing.';
                 break;
             }
         }
         if ($chunksize % 2 != 0) {
             // all structures are packed on word boundaries
             $chunksize++;
         }
         switch ($chunkname) {
             case 'LIST':
                 $listname = fread($fd, 4);
                 if (preg_match('#^(movi|rec )$#i', $listname)) {
                     $RIFFchunk[$listname]['offset'] = ftell($fd) - 4;
                     $RIFFchunk[$listname]['size'] = $chunksize;
                     if ($FoundAllChunksWeNeed) {
                         // skip over
                     } else {
                         $WhereWeWere = ftell($fd);
                         $AudioChunkHeader = fread($fd, 12);
                         $AudioChunkStreamNum = substr($AudioChunkHeader, 0, 2);
                         $AudioChunkStreamType = substr($AudioChunkHeader, 2, 2);
                         $AudioChunkSize = getid3_lib::LittleEndian2Int(substr($AudioChunkHeader, 4, 4));
                         if ($AudioChunkStreamType == 'wb') {
                             $FirstFourBytes = substr($AudioChunkHeader, 8, 4);
                             if (preg_match('/^\\xFF[\\xE2-\\xE7\\xF2-\\xF7\\xFA-\\xFF][\\x00-\\xEB]/s', $FirstFourBytes)) {
                                 // MP3
                                 if (getid3_mp3::MPEGaudioHeaderBytesValid($FirstFourBytes)) {
                                     $dummy = $ThisFileInfo;
                                     $dummy['avdataoffset'] = ftell($fd) - 4;
                                     $dummy['avdataend'] = ftell($fd) + $AudioChunkSize;
                                     getid3_mp3::getOnlyMPEGaudioInfo($fd, $dummy, $dummy['avdataoffset'], false);
                                     if (isset($dummy['mpeg']['audio'])) {
                                         $ThisFileInfo = $dummy;
                                         $ThisFileInfo['audio']['dataformat'] = 'mp' . $ThisFileInfo['mpeg']['audio']['layer'];
                                         $ThisFileInfo['audio']['sample_rate'] = $ThisFileInfo['mpeg']['audio']['sample_rate'];
                                         $ThisFileInfo['audio']['channels'] = $ThisFileInfo['mpeg']['audio']['channels'];
                                         $ThisFileInfo['audio']['bitrate'] = $ThisFileInfo['mpeg']['audio']['bitrate'];
                                         $ThisFileInfo['bitrate'] = $ThisFileInfo['audio']['bitrate'];
                                         $ThisFileInfo['audio']['bitrate_mode'] = strtolower($ThisFileInfo['mpeg']['audio']['bitrate_mode']);
                                     }
                                     unset($dummy);
                                 }
                             } elseif (preg_match('/^\\x0B\\x77/s', $FirstFourBytes)) {
                                 // AC3
                                 $GETID3_ERRORARRAY =& $ThisFileInfo['warning'];
                                 if (getid3_lib::IncludeDependency(GETID3_INCLUDEPATH . 'module.audio.ac3.php', __FILE__, false)) {
                                     $dummy = $ThisFileInfo;
                                     $dummy['avdataoffset'] = ftell($fd) - 4;
                                     $dummy['avdataend'] = ftell($fd) + $AudioChunkSize;
                                     $dummy['error'] = array();
                                     $ac3_tag = new getid3_ac3($fd, $dummy);
                                     if (empty($dummy['error'])) {
                                         $ThisFileInfo['audio'] = $dummy['audio'];
                                         $ThisFileInfo['ac3'] = $dummy['ac3'];
                                         $ThisFileInfo['warning'] = $dummy['warning'];
                                     }
                                     unset($ac3_tag);
                                 }
                             }
                         }
                         $FoundAllChunksWeNeed = true;
                         fseek($fd, $WhereWeWere, SEEK_SET);
                     }
                     fseek($fd, $chunksize - 4, SEEK_CUR);
                     //} elseif (preg_match('#^[0-9]{2}(wb|pc|dc|db)$#i', $listname)) {
                     //
                     //	// data chunk, ignore
                     //
                 } else {
                     if (!isset($RIFFchunk[$listname])) {
                         $RIFFchunk[$listname] = array();
                     }
                     $LISTchunkParent = $listname;
                     $LISTchunkMaxOffset = ftell($fd) - 4 + $chunksize;
                     if ($parsedChunk = getid3_riff::ParseRIFF($fd, ftell($fd), ftell($fd) + $chunksize - 4, $ThisFileInfo)) {
                         $RIFFchunk[$listname] = array_merge_recursive($RIFFchunk[$listname], $parsedChunk);
                     }
                 }
                 break;
             default:
                 if (preg_match('#^[0-9]{2}(wb|pc|dc|db)$#', $chunkname)) {
                     $nextoffset = ftell($fd) + $chunksize;
                     if ($nextoffset < 0 || !getid3_lib::intValueSupported($nextoffset)) {
                         $ThisFileInfo['warning'][] = 'Unable to parse chunk at offset ' . $nextoffset . ' because beyond ' . round(PHP_INT_MAX / 1073741824) . 'GB limit of PHP filesystem functions';
                         break 2;
                     }
                     fseek($fd, $nextoffset, SEEK_SET);
                     break;
                 }
                 $thisindex = 0;
                 if (isset($RIFFchunk[$chunkname]) && is_array($RIFFchunk[$chunkname])) {
                     $thisindex = count($RIFFchunk[$chunkname]);
                 }
                 $RIFFchunk[$chunkname][$thisindex]['offset'] = ftell($fd) - 8;
                 $RIFFchunk[$chunkname][$thisindex]['size'] = $chunksize;
                 switch ($chunkname) {
                     case 'data':
                         $ThisFileInfo['avdataoffset'] = ftell($fd);
                         $ThisFileInfo['avdataend'] = $ThisFileInfo['avdataoffset'] + $chunksize;
                         $RIFFdataChunkContentsTest = fread($fd, 36);
                         if (strlen($RIFFdataChunkContentsTest) > 0 && preg_match('/^\\xFF[\\xE2-\\xE7\\xF2-\\xF7\\xFA-\\xFF][\\x00-\\xEB]/s', substr($RIFFdataChunkContentsTest, 0, 4))) {
                             // Probably is MP3 data
                             if (getid3_mp3::MPEGaudioHeaderBytesValid(substr($RIFFdataChunkContentsTest, 0, 4))) {
                                 $dummy = $ThisFileInfo;
                                 // copy info array, only use if there's no error
                                 $getid3_mp3 = new getid3_mp3($fd, $dummy);
                                 $dummy = $ThisFileInfo;
                                 // copy info array, only use if there's no error
                                 $getid3_mp3->getOnlyMPEGaudioInfo($fd, $dummy, $RIFFchunk[$chunkname][$thisindex]['offset'], false);
                                 // use dummy array unless error
                                 if (empty($dummy['error'])) {
                                     $ThisFileInfo = $dummy;
                                 }
                             }
                         } elseif (strlen($RIFFdataChunkContentsTest) > 0 && substr($RIFFdataChunkContentsTest, 0, 2) == "\vw") {
                             // This is probably AC-3 data
                             $GETID3_ERRORARRAY =& $ThisFileInfo['warning'];
                             if (getid3_lib::IncludeDependency(GETID3_INCLUDEPATH . 'module.audio.ac3.php', __FILE__, false)) {
                                 $dummy = $ThisFileInfo;
                                 $dummy['avdataoffset'] = $RIFFchunk[$chunkname][$thisindex]['offset'];
                                 $dummy['avdataend'] = $dummy['avdataoffset'] + $RIFFchunk[$chunkname][$thisindex]['size'];
                                 $dummy['error'] = array();
                                 $ac3_tag = new getid3_ac3($fd, $dummy);
                                 if (empty($dummy['error'])) {
                                     $ThisFileInfo['audio'] = $dummy['audio'];
                                     $ThisFileInfo['ac3'] = $dummy['ac3'];
                                     $ThisFileInfo['warning'] = $dummy['warning'];
                                 }
                                 unset($ac3_tag);
                             }
                         } elseif (strlen($RIFFdataChunkContentsTest) > 0 && substr($RIFFdataChunkContentsTest, 8, 2) == "w\v") {
                             // Dolby Digital WAV
                             // AC-3 content, but not encoded in same format as normal AC-3 file
                             // For one thing, byte order is swapped
                             $GETID3_ERRORARRAY =& $ThisFileInfo['warning'];
                             if (getid3_lib::IncludeDependency(GETID3_INCLUDEPATH . 'module.audio.ac3.php', __FILE__, false)) {
                                 // ok to use tmpfile here - only 56 bytes
                                 if ($fd_temp = tmpfile()) {
                                     for ($i = 0; $i < 28; $i += 2) {
                                         // swap byte order
                                         fwrite($fd_temp, substr($RIFFdataChunkContentsTest, 8 + $i + 1, 1));
                                         fwrite($fd_temp, substr($RIFFdataChunkContentsTest, 8 + $i + 0, 1));
                                     }
                                     $dummy = $ThisFileInfo;
                                     $dummy['avdataoffset'] = 0;
                                     $dummy['avdataend'] = 20;
                                     $dummy['error'] = array();
                                     $ac3_tag = new getid3_ac3($fd_temp, $dummy);
                                     fclose($fd_temp);
                                     if (empty($dummy['error'])) {
                                         $ThisFileInfo['audio'] = $dummy['audio'];
                                         $ThisFileInfo['ac3'] = $dummy['ac3'];
                                         $ThisFileInfo['warning'] = $dummy['warning'];
                                     } else {
                                         $ThisFileInfo['error'][] = 'Errors parsing DolbyDigital WAV: ' . explode(';', $dummy['error']);
                                     }
                                     unset($ac3_tag);
                                 } else {
                                     $ThisFileInfo['error'][] = 'Could not create temporary file to analyze DolbyDigital WAV';
                                 }
                             }
                         } elseif (strlen($RIFFdataChunkContentsTest) > 0 && substr($RIFFdataChunkContentsTest, 0, 4) == 'wvpk') {
                             // This is WavPack data
                             $ThisFileInfo['wavpack']['offset'] = $RIFFchunk[$chunkname][$thisindex]['offset'];
                             $ThisFileInfo['wavpack']['size'] = getid3_lib::LittleEndian2Int(substr($RIFFdataChunkContentsTest, 4, 4));
                             getid3_riff::RIFFparseWavPackHeader(substr($RIFFdataChunkContentsTest, 8, 28), $ThisFileInfo);
                         } else {
                             // This is some other kind of data (quite possibly just PCM)
                             // do nothing special, just skip it
                         }
                         $nextoffset = $RIFFchunk[$chunkname][$thisindex]['offset'] + 8 + $chunksize;
                         if ($nextoffset < 0 || !getid3_lib::intValueSupported($nextoffset)) {
                             $ThisFileInfo['warning'][] = 'Unable to parse chunk at offset ' . $nextoffset . ' because beyond ' . round(PHP_INT_MAX / 1073741824) . 'GB limit of PHP filesystem functions';
                             break 3;
                         }
                         fseek($fd, $RIFFchunk[$chunkname][$thisindex]['offset'] + 8 + $chunksize, SEEK_SET);
                         break;
                     case 'bext':
                     case 'cart':
                     case 'fmt ':
                     case 'strh':
                     case 'strf':
                     case 'indx':
                     case 'MEXT':
                     case 'DISP':
                         // always read data in
                         $RIFFchunk[$chunkname][$thisindex]['data'] = fread($fd, $chunksize);
                         break;
                     case 'JUNK':
                         // never read data in
                         $nextoffset = ftell($fd) + $chunksize;
                         if ($nextoffset < 0 || !getid3_lib::intValueSupported($nextoffset)) {
                             $ThisFileInfo['warning'][] = 'Unable to parse chunk at offset ' . $nextoffset . ' because beyond ' . round(PHP_INT_MAX / 1073741824) . 'GB limit of PHP filesystem functions';
                             break 3;
                         }
                         fseek($fd, $nextoffset, SEEK_SET);
                         break;
                     default:
                         if (!preg_match('#^[0-9]{2}(wb|pc|dc|db)$#', $chunkname) && !empty($LISTchunkParent) && $RIFFchunk[$chunkname][$thisindex]['offset'] + $RIFFchunk[$chunkname][$thisindex]['size'] <= $LISTchunkMaxOffset) {
                             $RIFFchunk[$LISTchunkParent][$chunkname][$thisindex]['offset'] = $RIFFchunk[$chunkname][$thisindex]['offset'];
                             $RIFFchunk[$LISTchunkParent][$chunkname][$thisindex]['size'] = $RIFFchunk[$chunkname][$thisindex]['size'];
                             unset($RIFFchunk[$chunkname][$thisindex]['offset']);
                             unset($RIFFchunk[$chunkname][$thisindex]['size']);
                             if (isset($RIFFchunk[$chunkname][$thisindex]) && empty($RIFFchunk[$chunkname][$thisindex])) {
                                 unset($RIFFchunk[$chunkname][$thisindex]);
                             }
                             if (isset($RIFFchunk[$chunkname]) && empty($RIFFchunk[$chunkname])) {
                                 unset($RIFFchunk[$chunkname]);
                             }
                             $RIFFchunk[$LISTchunkParent][$chunkname][$thisindex]['data'] = fread($fd, $chunksize);
                         } elseif ($chunksize > 0 && $chunksize < 2048) {
                             // only read data in if smaller than 2kB
                             $RIFFchunk[$chunkname][$thisindex]['data'] = fread($fd, $chunksize);
                         } else {
                             $nextoffset = ftell($fd) + $chunksize;
                             if ($nextoffset < 0 || !getid3_lib::intValueSupported($nextoffset)) {
                                 $ThisFileInfo['warning'][] = 'Unable to parse chunk at offset ' . $nextoffset . ' because beyond ' . round(PHP_INT_MAX / 1073741824) . 'GB limit of PHP filesystem functions';
                                 break 3;
                             }
                             fseek($fd, $nextoffset, SEEK_SET);
                         }
                         break;
                 }
                 break;
         }
     }
     return $RIFFchunk;
 }
Exemplo n.º 16
0
 public function ParseRIFF($startoffset, $maxoffset)
 {
     $info =& $this->getid3->info;
     $RIFFchunk = false;
     $FoundAllChunksWeNeed = false;
     try {
         $this->fseek($startoffset);
         $maxoffset = min($maxoffset, $info['avdataend']);
         while ($this->ftell() < $maxoffset) {
             $chunknamesize = $this->fread(8);
             $chunkname = substr($chunknamesize, 0, 4);
             $chunksize = $this->EitherEndian2Int(substr($chunknamesize, 4, 4));
             if (strlen(trim($chunkname, "")) < 4) {
                 $this->error('Expecting chunk name at offset ' . ($this->ftell() - 8) . ' but found nothing. Aborting RIFF parsing.');
                 break;
             }
             if ($chunksize == 0 && $chunkname != 'JUNK') {
                 $this->warning('Chunk (' . $chunkname . ') size at offset ' . ($this->ftell() - 4) . ' is zero. Aborting RIFF parsing.');
                 break;
             }
             if ($chunksize % 2 != 0) {
                 // all structures are packed on word boundaries
                 $chunksize++;
             }
             switch ($chunkname) {
                 case 'LIST':
                     $listname = $this->fread(4);
                     if (preg_match('#^(movi|rec )$#i', $listname)) {
                         $RIFFchunk[$listname]['offset'] = $this->ftell() - 4;
                         $RIFFchunk[$listname]['size'] = $chunksize;
                         if (!$FoundAllChunksWeNeed) {
                             $WhereWeWere = $this->ftell();
                             $AudioChunkHeader = $this->fread(12);
                             $AudioChunkStreamNum = substr($AudioChunkHeader, 0, 2);
                             $AudioChunkStreamType = substr($AudioChunkHeader, 2, 2);
                             $AudioChunkSize = getid3_lib::LittleEndian2Int(substr($AudioChunkHeader, 4, 4));
                             if ($AudioChunkStreamType == 'wb') {
                                 $FirstFourBytes = substr($AudioChunkHeader, 8, 4);
                                 if (preg_match('/^\\xFF[\\xE2-\\xE7\\xF2-\\xF7\\xFA-\\xFF][\\x00-\\xEB]/s', $FirstFourBytes)) {
                                     // MP3
                                     if (getid3_mp3::MPEGaudioHeaderBytesValid($FirstFourBytes)) {
                                         $getid3_temp = new getID3();
                                         $getid3_temp->openfile($this->getid3->filename);
                                         $getid3_temp->info['avdataoffset'] = $this->ftell() - 4;
                                         $getid3_temp->info['avdataend'] = $this->ftell() + $AudioChunkSize;
                                         $getid3_mp3 = new getid3_mp3($getid3_temp);
                                         $getid3_mp3->getOnlyMPEGaudioInfo($getid3_temp->info['avdataoffset'], false);
                                         if (isset($getid3_temp->info['mpeg']['audio'])) {
                                             $info['mpeg']['audio'] = $getid3_temp->info['mpeg']['audio'];
                                             $info['audio'] = $getid3_temp->info['audio'];
                                             $info['audio']['dataformat'] = 'mp' . $info['mpeg']['audio']['layer'];
                                             $info['audio']['sample_rate'] = $info['mpeg']['audio']['sample_rate'];
                                             $info['audio']['channels'] = $info['mpeg']['audio']['channels'];
                                             $info['audio']['bitrate'] = $info['mpeg']['audio']['bitrate'];
                                             $info['audio']['bitrate_mode'] = strtolower($info['mpeg']['audio']['bitrate_mode']);
                                             //$info['bitrate']               = $info['audio']['bitrate'];
                                         }
                                         unset($getid3_temp, $getid3_mp3);
                                     }
                                 } elseif (strpos($FirstFourBytes, getid3_ac3::syncword) === 0) {
                                     // AC3
                                     $getid3_temp = new getID3();
                                     $getid3_temp->openfile($this->getid3->filename);
                                     $getid3_temp->info['avdataoffset'] = $this->ftell() - 4;
                                     $getid3_temp->info['avdataend'] = $this->ftell() + $AudioChunkSize;
                                     $getid3_ac3 = new getid3_ac3($getid3_temp);
                                     $getid3_ac3->Analyze();
                                     if (empty($getid3_temp->info['error'])) {
                                         $info['audio'] = $getid3_temp->info['audio'];
                                         $info['ac3'] = $getid3_temp->info['ac3'];
                                         if (!empty($getid3_temp->info['warning'])) {
                                             foreach ($getid3_temp->info['warning'] as $key => $value) {
                                                 $info['warning'][] = $value;
                                             }
                                         }
                                     }
                                     unset($getid3_temp, $getid3_ac3);
                                 }
                             }
                             $FoundAllChunksWeNeed = true;
                             $this->fseek($WhereWeWere);
                         }
                         $this->fseek($chunksize - 4, SEEK_CUR);
                     } else {
                         if (!isset($RIFFchunk[$listname])) {
                             $RIFFchunk[$listname] = array();
                         }
                         $LISTchunkParent = $listname;
                         $LISTchunkMaxOffset = $this->ftell() - 4 + $chunksize;
                         if ($parsedChunk = $this->ParseRIFF($this->ftell(), $LISTchunkMaxOffset)) {
                             $RIFFchunk[$listname] = array_merge_recursive($RIFFchunk[$listname], $parsedChunk);
                         }
                     }
                     break;
                 default:
                     if (preg_match('#^[0-9]{2}(wb|pc|dc|db)$#', $chunkname)) {
                         $this->fseek($chunksize, SEEK_CUR);
                         break;
                     }
                     $thisindex = 0;
                     if (isset($RIFFchunk[$chunkname]) && is_array($RIFFchunk[$chunkname])) {
                         $thisindex = count($RIFFchunk[$chunkname]);
                     }
                     $RIFFchunk[$chunkname][$thisindex]['offset'] = $this->ftell() - 8;
                     $RIFFchunk[$chunkname][$thisindex]['size'] = $chunksize;
                     switch ($chunkname) {
                         case 'data':
                             $info['avdataoffset'] = $this->ftell();
                             $info['avdataend'] = $info['avdataoffset'] + $chunksize;
                             $RIFFdataChunkContentsTest = $this->fread(36);
                             if (strlen($RIFFdataChunkContentsTest) > 0 && preg_match('/^\\xFF[\\xE2-\\xE7\\xF2-\\xF7\\xFA-\\xFF][\\x00-\\xEB]/s', substr($RIFFdataChunkContentsTest, 0, 4))) {
                                 // Probably is MP3 data
                                 if (getid3_mp3::MPEGaudioHeaderBytesValid(substr($RIFFdataChunkContentsTest, 0, 4))) {
                                     $getid3_temp = new getID3();
                                     $getid3_temp->openfile($this->getid3->filename);
                                     $getid3_temp->info['avdataoffset'] = $RIFFchunk[$chunkname][$thisindex]['offset'];
                                     $getid3_temp->info['avdataend'] = $RIFFchunk[$chunkname][$thisindex]['offset'] + $RIFFchunk[$chunkname][$thisindex]['size'];
                                     $getid3_mp3 = new getid3_mp3($getid3_temp);
                                     $getid3_mp3->getOnlyMPEGaudioInfo($RIFFchunk[$chunkname][$thisindex]['offset'], false);
                                     if (empty($getid3_temp->info['error'])) {
                                         $info['mpeg'] = $getid3_temp->info['mpeg'];
                                         $info['audio'] = $getid3_temp->info['audio'];
                                     }
                                     unset($getid3_temp, $getid3_mp3);
                                 }
                             } elseif (strlen($RIFFdataChunkContentsTest) > 0 && substr($RIFFdataChunkContentsTest, 0, 2) == getid3_ac3::syncword) {
                                 // This is probably AC-3 data
                                 $getid3_temp = new getID3();
                                 $getid3_temp->openfile($this->getid3->filename);
                                 $getid3_temp->info['avdataoffset'] = $RIFFchunk[$chunkname][$thisindex]['offset'];
                                 $getid3_temp->info['avdataend'] = $RIFFchunk[$chunkname][$thisindex]['offset'] + $RIFFchunk[$chunkname][$thisindex]['size'];
                                 $getid3_ac3 = new getid3_ac3($getid3_temp);
                                 $getid3_ac3->Analyze();
                                 if (empty($getid3_temp->info['error'])) {
                                     $info['audio'] = $getid3_temp->info['audio'];
                                     $info['ac3'] = $getid3_temp->info['ac3'];
                                     $info['warning'] = $getid3_temp->info['warning'];
                                 }
                                 unset($getid3_temp, $getid3_ac3);
                             } elseif (strlen($RIFFdataChunkContentsTest) > 0 && substr($RIFFdataChunkContentsTest, 8, 2) == getid3_ac3::syncword) {
                                 // Dolby Digital WAV
                                 // AC-3 content, but not encoded in same format as normal AC-3 file
                                 // For one thing, byte order is swapped
                                 if ($RIFFtempfilename = tempnam(GETID3_TEMP_DIR, 'id3')) {
                                     // ok to use tmpfile here - only 56 bytes
                                     if ($fd_temp = fopen($RIFFtempfilename, 'wb')) {
                                         for ($i = 0; $i < 28; $i += 2) {
                                             // swap byte order
                                             fwrite($fd_temp, substr($RIFFdataChunkContentsTest, 8 + $i + 1, 1));
                                             fwrite($fd_temp, substr($RIFFdataChunkContentsTest, 8 + $i + 0, 1));
                                         }
                                         fclose($fd_temp);
                                         $getid3_temp = new getID3();
                                         $getid3_temp->openfile($RIFFtempfilename);
                                         $getid3_temp->info['avdataend'] = 20;
                                         $getid3_ac3 = new getid3_ac3($getid3_temp);
                                         $getid3_ac3->Analyze();
                                         if (empty($getid3_temp->info['error'])) {
                                             $info['audio'] = $getid3_temp->info['audio'];
                                             $info['ac3'] = $getid3_temp->info['ac3'];
                                             $info['warning'] = $getid3_temp->info['warning'];
                                         } else {
                                             $info['error'][] = 'Error parsing Dolby Digital WAV (AC3-in-RIFF): ' . implode(';', $getid3_temp->info['error']);
                                         }
                                         unset($getid3_ac3, $getid3_temp);
                                     } else {
                                         $info['error'][] = 'Error parsing Dolby Digital WAV (AC3-in-RIFF): failed to write temp file';
                                     }
                                     unlink($RIFFtempfilename);
                                 } else {
                                     $info['error'][] = 'Error parsing Dolby Digital WAV (AC3-in-RIFF): failed to write temp file';
                                 }
                             } elseif (strlen($RIFFdataChunkContentsTest) > 0 && substr($RIFFdataChunkContentsTest, 0, 4) == 'wvpk') {
                                 // This is WavPack data
                                 $info['wavpack']['offset'] = $RIFFchunk[$chunkname][$thisindex]['offset'];
                                 $info['wavpack']['size'] = getid3_lib::LittleEndian2Int(substr($RIFFdataChunkContentsTest, 4, 4));
                                 self::parseWavPackHeader(substr($RIFFdataChunkContentsTest, 8, 28));
                             } else {
                                 // This is some other kind of data (quite possibly just PCM)
                                 // do nothing special, just skip it
                             }
                             $nextoffset = $RIFFchunk[$chunkname][$thisindex]['offset'] + 8 + $chunksize;
                             $this->fseek($nextoffset);
                             break;
                         case 'iXML':
                         case 'bext':
                         case 'cart':
                         case 'fmt ':
                         case 'strh':
                         case 'strf':
                         case 'indx':
                         case 'MEXT':
                         case 'DISP':
                             // always read data in
                         // always read data in
                         case 'JUNK':
                             // should be: never read data in
                             // but some programs write their version strings in a JUNK chunk (e.g. VirtualDub, AVIdemux, etc)
                             if ($chunksize < 1048576) {
                                 if ($chunksize > 0) {
                                     $RIFFchunk[$chunkname][$thisindex]['data'] = $this->fread($chunksize);
                                     if ($chunkname == 'JUNK') {
                                         if (preg_match('#^([\\x20-\\x7F]+)#', $RIFFchunk[$chunkname][$thisindex]['data'], $matches)) {
                                             // only keep text characters [chr(32)-chr(127)]
                                             $info['riff']['comments']['junk'][] = trim($matches[1]);
                                         }
                                         // but if nothing there, ignore
                                         // remove the key in either case
                                         unset($RIFFchunk[$chunkname][$thisindex]['data']);
                                     }
                                 }
                             } else {
                                 $this->warning('Chunk "' . $chunkname . '" at offset ' . $this->ftell() . ' is unexpectedly larger than 1MB (claims to be ' . number_format($chunksize) . ' bytes), skipping data');
                                 $this->fseek($chunksize, SEEK_CUR);
                             }
                             break;
                             //case 'IDVX':
                             //	$info['divxtag']['comments'] = self::ParseDIVXTAG($this->fread($chunksize));
                             //	break;
                         //case 'IDVX':
                         //	$info['divxtag']['comments'] = self::ParseDIVXTAG($this->fread($chunksize));
                         //	break;
                         default:
                             if (!empty($LISTchunkParent) && $RIFFchunk[$chunkname][$thisindex]['offset'] + $RIFFchunk[$chunkname][$thisindex]['size'] <= $LISTchunkMaxOffset) {
                                 $RIFFchunk[$LISTchunkParent][$chunkname][$thisindex]['offset'] = $RIFFchunk[$chunkname][$thisindex]['offset'];
                                 $RIFFchunk[$LISTchunkParent][$chunkname][$thisindex]['size'] = $RIFFchunk[$chunkname][$thisindex]['size'];
                                 unset($RIFFchunk[$chunkname][$thisindex]['offset']);
                                 unset($RIFFchunk[$chunkname][$thisindex]['size']);
                                 if (isset($RIFFchunk[$chunkname][$thisindex]) && empty($RIFFchunk[$chunkname][$thisindex])) {
                                     unset($RIFFchunk[$chunkname][$thisindex]);
                                 }
                                 if (isset($RIFFchunk[$chunkname]) && empty($RIFFchunk[$chunkname])) {
                                     unset($RIFFchunk[$chunkname]);
                                 }
                                 $RIFFchunk[$LISTchunkParent][$chunkname][$thisindex]['data'] = $this->fread($chunksize);
                             } elseif ($chunksize < 2048) {
                                 // only read data in if smaller than 2kB
                                 $RIFFchunk[$chunkname][$thisindex]['data'] = $this->fread($chunksize);
                             } else {
                                 $this->fseek($chunksize, SEEK_CUR);
                             }
                             break;
                     }
                     break;
             }
         }
     } catch (getid3_exception $e) {
         if ($e->getCode() == 10) {
             $this->warning('RIFF parser: ' . $e->getMessage());
         } else {
             throw $e;
         }
     }
     return $RIFFchunk;
 }