예제 #1
0
 private function parseEBML(&$info)
 {
     // http://www.matroska.org/technical/specs/index.html#EBMLBasics
     $this->current_offset = $info['avdataoffset'];
     while ($this->getEBMLelement($top_element, $info['avdataend'])) {
         switch ($top_element['id']) {
             case self::ID_EBML:
                 $info['matroska']['header']['offset'] = $top_element['offset'];
                 $info['matroska']['header']['length'] = $top_element['length'];
                 while ($this->getEBMLelement($element_data, $top_element['end'], true)) {
                     switch ($element_data['id']) {
                         case self::ID_EBMLVERSION:
                         case self::ID_EBMLREADVERSION:
                         case self::ID_EBMLMAXIDLENGTH:
                         case self::ID_EBMLMAXSIZELENGTH:
                         case self::ID_DOCTYPEVERSION:
                         case self::ID_DOCTYPEREADVERSION:
                             $element_data['data'] = Utils::BigEndian2Int($element_data['data']);
                             break;
                         case self::ID_DOCTYPE:
                             $element_data['data'] = Utils::trimNullByte($element_data['data']);
                             $info['matroska']['doctype'] = $element_data['data'];
                             $info['fileformat'] = $element_data['data'];
                             break;
                         default:
                             $this->unhandledElement('header', __LINE__, $element_data);
                     }
                     unset($element_data['offset'], $element_data['end']);
                     $info['matroska']['header']['elements'][] = $element_data;
                 }
                 break;
             case self::ID_SEGMENT:
                 $info['matroska']['segment'][0]['offset'] = $top_element['offset'];
                 $info['matroska']['segment'][0]['length'] = $top_element['length'];
                 while ($this->getEBMLelement($element_data, $top_element['end'])) {
                     if ($element_data['id'] != self::ID_CLUSTER || !self::$hide_clusters) {
                         // collect clusters only if required
                         $info['matroska']['segments'][] = $element_data;
                     }
                     switch ($element_data['id']) {
                         case self::ID_SEEKHEAD:
                             // Contains the position of other level 1 elements.
                             while ($this->getEBMLelement($seek_entry, $element_data['end'])) {
                                 switch ($seek_entry['id']) {
                                     case self::ID_SEEK:
                                         // Contains a single seek entry to an EBML element
                                         while ($this->getEBMLelement($sub_seek_entry, $seek_entry['end'], true)) {
                                             switch ($sub_seek_entry['id']) {
                                                 case self::ID_SEEKID:
                                                     $seek_entry['target_id'] = self::EBML2Int($sub_seek_entry['data']);
                                                     $seek_entry['target_name'] = self::EBMLidName($seek_entry['target_id']);
                                                     break;
                                                 case self::ID_SEEKPOSITION:
                                                     $seek_entry['target_offset'] = $element_data['offset'] + Utils::BigEndian2Int($sub_seek_entry['data']);
                                                     break;
                                                 default:
                                                     $this->unhandledElement('seekhead.seek', __LINE__, $sub_seek_entry);
                                             }
                                         }
                                         if ($seek_entry['target_id'] != self::ID_CLUSTER || !self::$hide_clusters) {
                                             // collect clusters only if required
                                             $info['matroska']['seek'][] = $seek_entry;
                                         }
                                         break;
                                     default:
                                         $this->unhandledElement('seekhead', __LINE__, $seek_entry);
                                 }
                             }
                             break;
                         case self::ID_TRACKS:
                             // A top-level block of information with many tracks described.
                             $info['matroska']['tracks'] = $element_data;
                             while ($this->getEBMLelement($track_entry, $element_data['end'])) {
                                 switch ($track_entry['id']) {
                                     case self::ID_TRACKENTRY:
                                         //subelements: Describes a track with all elements.
                                         while ($this->getEBMLelement($subelement, $track_entry['end'], [self::ID_VIDEO, self::ID_AUDIO, self::ID_CONTENTENCODINGS, self::ID_CODECPRIVATE])) {
                                             switch ($subelement['id']) {
                                                 case self::ID_TRACKNUMBER:
                                                 case self::ID_TRACKUID:
                                                 case self::ID_TRACKTYPE:
                                                 case self::ID_MINCACHE:
                                                 case self::ID_MAXCACHE:
                                                 case self::ID_MAXBLOCKADDITIONID:
                                                 case self::ID_DEFAULTDURATION:
                                                     // nanoseconds per frame
                                                     $track_entry[$subelement['id_name']] = Utils::BigEndian2Int($subelement['data']);
                                                     break;
                                                 case self::ID_TRACKTIMECODESCALE:
                                                     $track_entry[$subelement['id_name']] = Utils::BigEndian2Float($subelement['data']);
                                                     break;
                                                 case self::ID_CODECID:
                                                 case self::ID_LANGUAGE:
                                                 case self::ID_NAME:
                                                 case self::ID_CODECNAME:
                                                     $track_entry[$subelement['id_name']] = Utils::trimNullByte($subelement['data']);
                                                     break;
                                                 case self::ID_CODECPRIVATE:
                                                     $track_entry[$subelement['id_name']] = $this->readEBMLelementData($subelement['length'], true);
                                                     break;
                                                 case self::ID_FLAGENABLED:
                                                 case self::ID_FLAGDEFAULT:
                                                 case self::ID_FLAGFORCED:
                                                 case self::ID_FLAGLACING:
                                                 case self::ID_CODECDECODEALL:
                                                     $track_entry[$subelement['id_name']] = (bool) Utils::BigEndian2Int($subelement['data']);
                                                     break;
                                                 case self::ID_VIDEO:
                                                     while ($this->getEBMLelement($sub_subelement, $subelement['end'], true)) {
                                                         switch ($sub_subelement['id']) {
                                                             case self::ID_PIXELWIDTH:
                                                             case self::ID_PIXELHEIGHT:
                                                             case self::ID_PIXELCROPBOTTOM:
                                                             case self::ID_PIXELCROPTOP:
                                                             case self::ID_PIXELCROPLEFT:
                                                             case self::ID_PIXELCROPRIGHT:
                                                             case self::ID_DISPLAYWIDTH:
                                                             case self::ID_DISPLAYHEIGHT:
                                                             case self::ID_DISPLAYUNIT:
                                                             case self::ID_ASPECTRATIOTYPE:
                                                             case self::ID_STEREOMODE:
                                                             case self::ID_OLDSTEREOMODE:
                                                                 $track_entry[$sub_subelement['id_name']] = Utils::BigEndian2Int($sub_subelement['data']);
                                                                 break;
                                                             case self::ID_FLAGINTERLACED:
                                                                 $track_entry[$sub_subelement['id_name']] = (bool) Utils::BigEndian2Int($sub_subelement['data']);
                                                                 break;
                                                             case self::ID_GAMMAVALUE:
                                                                 $track_entry[$sub_subelement['id_name']] = Utils::BigEndian2Float($sub_subelement['data']);
                                                                 break;
                                                             case self::ID_COLOURSPACE:
                                                                 $track_entry[$sub_subelement['id_name']] = Utils::trimNullByte($sub_subelement['data']);
                                                                 break;
                                                             default:
                                                                 $this->unhandledElement('track.video', __LINE__, $sub_subelement);
                                                         }
                                                     }
                                                     break;
                                                 case self::ID_AUDIO:
                                                     while ($this->getEBMLelement($sub_subelement, $subelement['end'], true)) {
                                                         switch ($sub_subelement['id']) {
                                                             case self::ID_CHANNELS:
                                                             case self::ID_BITDEPTH:
                                                                 $track_entry[$sub_subelement['id_name']] = Utils::BigEndian2Int($sub_subelement['data']);
                                                                 break;
                                                             case self::ID_SAMPLINGFREQUENCY:
                                                             case self::ID_OUTPUTSAMPLINGFREQUENCY:
                                                                 $track_entry[$sub_subelement['id_name']] = Utils::BigEndian2Float($sub_subelement['data']);
                                                                 break;
                                                             case self::ID_CHANNELPOSITIONS:
                                                                 $track_entry[$sub_subelement['id_name']] = Utils::trimNullByte($sub_subelement['data']);
                                                                 break;
                                                             default:
                                                                 $this->unhandledElement('track.audio', __LINE__, $sub_subelement);
                                                         }
                                                     }
                                                     break;
                                                 case self::ID_CONTENTENCODINGS:
                                                     while ($this->getEBMLelement($sub_subelement, $subelement['end'])) {
                                                         switch ($sub_subelement['id']) {
                                                             case self::ID_CONTENTENCODING:
                                                                 while ($this->getEBMLelement($sub_sub_subelement, $sub_subelement['end'], [self::ID_CONTENTCOMPRESSION, self::ID_CONTENTENCRYPTION])) {
                                                                     switch ($sub_sub_subelement['id']) {
                                                                         case self::ID_CONTENTENCODINGORDER:
                                                                         case self::ID_CONTENTENCODINGSCOPE:
                                                                         case self::ID_CONTENTENCODINGTYPE:
                                                                             $track_entry[$sub_subelement['id_name']][$sub_sub_subelement['id_name']] = Utils::BigEndian2Int($sub_sub_subelement['data']);
                                                                             break;
                                                                         case self::ID_CONTENTCOMPRESSION:
                                                                             while ($this->getEBMLelement($sub_sub_sub_subelement, $sub_sub_subelement['end'], true)) {
                                                                                 switch ($sub_sub_sub_subelement['id']) {
                                                                                     case self::ID_CONTENTCOMPALGO:
                                                                                         $track_entry[$sub_subelement['id_name']][$sub_sub_subelement['id_name']][$sub_sub_sub_subelement['id_name']] = Utils::BigEndian2Int($sub_sub_sub_subelement['data']);
                                                                                         break;
                                                                                     case self::ID_CONTENTCOMPSETTINGS:
                                                                                         $track_entry[$sub_subelement['id_name']][$sub_sub_subelement['id_name']][$sub_sub_sub_subelement['id_name']] = $sub_sub_sub_subelement['data'];
                                                                                         break;
                                                                                     default:
                                                                                         $this->unhandledElement('track.contentencodings.contentencoding.contentcompression', __LINE__, $sub_sub_sub_subelement);
                                                                                 }
                                                                             }
                                                                             break;
                                                                         case self::ID_CONTENTENCRYPTION:
                                                                             while ($this->getEBMLelement($sub_sub_sub_subelement, $sub_sub_subelement['end'], true)) {
                                                                                 switch ($sub_sub_sub_subelement['id']) {
                                                                                     case self::ID_CONTENTENCALGO:
                                                                                     case self::ID_CONTENTSIGALGO:
                                                                                     case self::ID_CONTENTSIGHASHALGO:
                                                                                         $track_entry[$sub_subelement['id_name']][$sub_sub_subelement['id_name']][$sub_sub_sub_subelement['id_name']] = Utils::BigEndian2Int($sub_sub_sub_subelement['data']);
                                                                                         break;
                                                                                     case self::ID_CONTENTENCKEYID:
                                                                                     case self::ID_CONTENTSIGNATURE:
                                                                                     case self::ID_CONTENTSIGKEYID:
                                                                                         $track_entry[$sub_subelement['id_name']][$sub_sub_subelement['id_name']][$sub_sub_sub_subelement['id_name']] = $sub_sub_sub_subelement['data'];
                                                                                         break;
                                                                                     default:
                                                                                         $this->unhandledElement('track.contentencodings.contentencoding.contentcompression', __LINE__, $sub_sub_sub_subelement);
                                                                                 }
                                                                             }
                                                                             break;
                                                                         default:
                                                                             $this->unhandledElement('track.contentencodings.contentencoding', __LINE__, $sub_sub_subelement);
                                                                     }
                                                                 }
                                                                 break;
                                                             default:
                                                                 $this->unhandledElement('track.contentencodings', __LINE__, $sub_subelement);
                                                         }
                                                     }
                                                     break;
                                                 default:
                                                     $this->unhandledElement('track', __LINE__, $subelement);
                                             }
                                         }
                                         $info['matroska']['tracks']['tracks'][] = $track_entry;
                                         break;
                                     default:
                                         $this->unhandledElement('tracks', __LINE__, $track_entry);
                                 }
                             }
                             break;
                         case self::ID_INFO:
                             // Contains miscellaneous general information and statistics on the file.
                             $info_entry = [];
                             while ($this->getEBMLelement($subelement, $element_data['end'], true)) {
                                 switch ($subelement['id']) {
                                     case self::ID_TIMECODESCALE:
                                         $info_entry[$subelement['id_name']] = Utils::BigEndian2Int($subelement['data']);
                                         break;
                                     case self::ID_DURATION:
                                         $info_entry[$subelement['id_name']] = Utils::BigEndian2Float($subelement['data']);
                                         break;
                                     case self::ID_DATEUTC:
                                         $info_entry[$subelement['id_name']] = Utils::BigEndian2Int($subelement['data']);
                                         $info_entry[$subelement['id_name'] . '_unix'] = self::EBMLdate2unix($info_entry[$subelement['id_name']]);
                                         break;
                                     case self::ID_SEGMENTUID:
                                     case self::ID_PREVUID:
                                     case self::ID_NEXTUID:
                                         $info_entry[$subelement['id_name']] = Utils::trimNullByte($subelement['data']);
                                         break;
                                     case self::ID_SEGMENTFAMILY:
                                         $info_entry[$subelement['id_name']][] = Utils::trimNullByte($subelement['data']);
                                         break;
                                     case self::ID_SEGMENTFILENAME:
                                     case self::ID_PREVFILENAME:
                                     case self::ID_NEXTFILENAME:
                                     case self::ID_TITLE:
                                     case self::ID_MUXINGAPP:
                                     case self::ID_WRITINGAPP:
                                         $info_entry[$subelement['id_name']] = Utils::trimNullByte($subelement['data']);
                                         $info['matroska']['comments'][strtolower($subelement['id_name'])][] = $info_entry[$subelement['id_name']];
                                         break;
                                     case self::ID_CHAPTERTRANSLATE:
                                         $chaptertranslate_entry = [];
                                         while ($this->getEBMLelement($sub_subelement, $subelement['end'], true)) {
                                             switch ($sub_subelement['id']) {
                                                 case self::ID_CHAPTERTRANSLATEEDITIONUID:
                                                     $chaptertranslate_entry[$sub_subelement['id_name']][] = Utils::BigEndian2Int($sub_subelement['data']);
                                                     break;
                                                 case self::ID_CHAPTERTRANSLATECODEC:
                                                     $chaptertranslate_entry[$sub_subelement['id_name']] = Utils::BigEndian2Int($sub_subelement['data']);
                                                     break;
                                                 case self::ID_CHAPTERTRANSLATEID:
                                                     $chaptertranslate_entry[$sub_subelement['id_name']] = Utils::trimNullByte($sub_subelement['data']);
                                                     break;
                                                 default:
                                                     $this->unhandledElement('info.chaptertranslate', __LINE__, $sub_subelement);
                                             }
                                         }
                                         $info_entry[$subelement['id_name']] = $chaptertranslate_entry;
                                         break;
                                     default:
                                         $this->unhandledElement('info', __LINE__, $subelement);
                                 }
                             }
                             $info['matroska']['info'][] = $info_entry;
                             break;
                         case self::ID_CUES:
                             // A top-level element to speed seeking access. All entries are local to the segment. Should be mandatory for non "live" streams.
                             if (self::$hide_clusters) {
                                 // do not parse cues if hide clusters is "ON" till they point to clusters anyway
                                 $this->current_offset = $element_data['end'];
                                 break;
                             }
                             $cues_entry = [];
                             while ($this->getEBMLelement($subelement, $element_data['end'])) {
                                 switch ($subelement['id']) {
                                     case self::ID_CUEPOINT:
                                         $cuepoint_entry = [];
                                         while ($this->getEBMLelement($sub_subelement, $subelement['end'], [self::ID_CUETRACKPOSITIONS])) {
                                             switch ($sub_subelement['id']) {
                                                 case self::ID_CUETRACKPOSITIONS:
                                                     $cuetrackpositions_entry = [];
                                                     while ($this->getEBMLelement($sub_sub_subelement, $sub_subelement['end'], true)) {
                                                         switch ($sub_sub_subelement['id']) {
                                                             case self::ID_CUETRACK:
                                                             case self::ID_CUECLUSTERPOSITION:
                                                             case self::ID_CUEBLOCKNUMBER:
                                                             case self::ID_CUECODECSTATE:
                                                                 $cuetrackpositions_entry[$sub_sub_subelement['id_name']] = Utils::BigEndian2Int($sub_sub_subelement['data']);
                                                                 break;
                                                             default:
                                                                 $this->unhandledElement('cues.cuepoint.cuetrackpositions', __LINE__, $sub_sub_subelement);
                                                         }
                                                     }
                                                     $cuepoint_entry[$sub_subelement['id_name']][] = $cuetrackpositions_entry;
                                                     break;
                                                 case self::ID_CUETIME:
                                                     $cuepoint_entry[$sub_subelement['id_name']] = Utils::BigEndian2Int($sub_subelement['data']);
                                                     break;
                                                 default:
                                                     $this->unhandledElement('cues.cuepoint', __LINE__, $sub_subelement);
                                             }
                                         }
                                         $cues_entry[] = $cuepoint_entry;
                                         break;
                                     default:
                                         $this->unhandledElement('cues', __LINE__, $subelement);
                                 }
                             }
                             $info['matroska']['cues'] = $cues_entry;
                             break;
                         case self::ID_TAGS:
                             // Element containing elements specific to Tracks/Chapters.
                             $tags_entry = [];
                             while ($this->getEBMLelement($subelement, $element_data['end'], false)) {
                                 switch ($subelement['id']) {
                                     case self::ID_TAG:
                                         $tag_entry = [];
                                         while ($this->getEBMLelement($sub_subelement, $subelement['end'], false)) {
                                             switch ($sub_subelement['id']) {
                                                 case self::ID_TARGETS:
                                                     $targets_entry = [];
                                                     while ($this->getEBMLelement($sub_sub_subelement, $sub_subelement['end'], true)) {
                                                         switch ($sub_sub_subelement['id']) {
                                                             case self::ID_TARGETTYPEVALUE:
                                                                 $targets_entry[$sub_sub_subelement['id_name']] = Utils::BigEndian2Int($sub_sub_subelement['data']);
                                                                 $targets_entry[strtolower($sub_sub_subelement['id_name']) . '_long'] = self::TargetTypeValue($targets_entry[$sub_sub_subelement['id_name']]);
                                                                 break;
                                                             case self::ID_TARGETTYPE:
                                                                 $targets_entry[$sub_sub_subelement['id_name']] = $sub_sub_subelement['data'];
                                                                 break;
                                                             case self::ID_TAGTRACKUID:
                                                             case self::ID_TAGEDITIONUID:
                                                             case self::ID_TAGCHAPTERUID:
                                                             case self::ID_TAGATTACHMENTUID:
                                                                 $targets_entry[$sub_sub_subelement['id_name']][] = Utils::BigEndian2Int($sub_sub_subelement['data']);
                                                                 break;
                                                             default:
                                                                 $this->unhandledElement('tags.tag.targets', __LINE__, $sub_sub_subelement);
                                                         }
                                                     }
                                                     $tag_entry[$sub_subelement['id_name']] = $targets_entry;
                                                     break;
                                                 case self::ID_SIMPLETAG:
                                                     $tag_entry[$sub_subelement['id_name']][] = $this->HandleEMBLSimpleTag($sub_subelement['end']);
                                                     break;
                                                 default:
                                                     $this->unhandledElement('tags.tag', __LINE__, $sub_subelement);
                                             }
                                         }
                                         $tags_entry[] = $tag_entry;
                                         break;
                                     default:
                                         $this->unhandledElement('tags', __LINE__, $subelement);
                                 }
                             }
                             $info['matroska']['tags'] = $tags_entry;
                             break;
                         case self::ID_ATTACHMENTS:
                             // Contain attached files.
                             while ($this->getEBMLelement($subelement, $element_data['end'])) {
                                 switch ($subelement['id']) {
                                     case self::ID_ATTACHEDFILE:
                                         $attachedfile_entry = [];
                                         while ($this->getEBMLelement($sub_subelement, $subelement['end'], [self::ID_FILEDATA])) {
                                             switch ($sub_subelement['id']) {
                                                 case self::ID_FILEDESCRIPTION:
                                                 case self::ID_FILENAME:
                                                 case self::ID_FILEMIMETYPE:
                                                     $attachedfile_entry[$sub_subelement['id_name']] = $sub_subelement['data'];
                                                     break;
                                                 case self::ID_FILEDATA:
                                                     $attachedfile_entry['data_offset'] = $this->current_offset;
                                                     $attachedfile_entry['data_length'] = $sub_subelement['length'];
                                                     $attachedfile_entry[$sub_subelement['id_name']] = $this->saveAttachment($attachedfile_entry['FileName'], $attachedfile_entry['data_offset'], $attachedfile_entry['data_length']);
                                                     $this->current_offset = $sub_subelement['end'];
                                                     break;
                                                 case self::ID_FILEUID:
                                                     $attachedfile_entry[$sub_subelement['id_name']] = Utils::BigEndian2Int($sub_subelement['data']);
                                                     break;
                                                 default:
                                                     $this->unhandledElement('attachments.attachedfile', __LINE__, $sub_subelement);
                                             }
                                         }
                                         $info['matroska']['attachments'][] = $attachedfile_entry;
                                         break;
                                     default:
                                         $this->unhandledElement('attachments', __LINE__, $subelement);
                                 }
                             }
                             break;
                         case self::ID_CHAPTERS:
                             while ($this->getEBMLelement($subelement, $element_data['end'])) {
                                 switch ($subelement['id']) {
                                     case self::ID_EDITIONENTRY:
                                         $editionentry_entry = [];
                                         while ($this->getEBMLelement($sub_subelement, $subelement['end'], [self::ID_CHAPTERATOM])) {
                                             switch ($sub_subelement['id']) {
                                                 case self::ID_EDITIONUID:
                                                     $editionentry_entry[$sub_subelement['id_name']] = Utils::BigEndian2Int($sub_subelement['data']);
                                                     break;
                                                 case self::ID_EDITIONFLAGHIDDEN:
                                                 case self::ID_EDITIONFLAGDEFAULT:
                                                 case self::ID_EDITIONFLAGORDERED:
                                                     $editionentry_entry[$sub_subelement['id_name']] = (bool) Utils::BigEndian2Int($sub_subelement['data']);
                                                     break;
                                                 case self::ID_CHAPTERATOM:
                                                     $chapteratom_entry = [];
                                                     while ($this->getEBMLelement($sub_sub_subelement, $sub_subelement['end'], [self::ID_CHAPTERTRACK, self::ID_CHAPTERDISPLAY])) {
                                                         switch ($sub_sub_subelement['id']) {
                                                             case self::ID_CHAPTERSEGMENTUID:
                                                             case self::ID_CHAPTERSEGMENTEDITIONUID:
                                                                 $chapteratom_entry[$sub_sub_subelement['id_name']] = $sub_sub_subelement['data'];
                                                                 break;
                                                             case self::ID_CHAPTERFLAGENABLED:
                                                             case self::ID_CHAPTERFLAGHIDDEN:
                                                                 $chapteratom_entry[$sub_sub_subelement['id_name']] = (bool) Utils::BigEndian2Int($sub_sub_subelement['data']);
                                                                 break;
                                                             case self::ID_CHAPTERUID:
                                                             case self::ID_CHAPTERTIMESTART:
                                                             case self::ID_CHAPTERTIMEEND:
                                                                 $chapteratom_entry[$sub_sub_subelement['id_name']] = Utils::BigEndian2Int($sub_sub_subelement['data']);
                                                                 break;
                                                             case self::ID_CHAPTERTRACK:
                                                                 $chaptertrack_entry = [];
                                                                 while ($this->getEBMLelement($sub_sub_sub_subelement, $sub_sub_subelement['end'], true)) {
                                                                     switch ($sub_sub_sub_subelement['id']) {
                                                                         case self::ID_CHAPTERTRACKNUMBER:
                                                                             $chaptertrack_entry[$sub_sub_sub_subelement['id_name']] = Utils::BigEndian2Int($sub_sub_sub_subelement['data']);
                                                                             break;
                                                                         default:
                                                                             $this->unhandledElement('chapters.editionentry.chapteratom.chaptertrack', __LINE__, $sub_sub_sub_subelement);
                                                                     }
                                                                 }
                                                                 $chapteratom_entry[$sub_sub_subelement['id_name']][] = $chaptertrack_entry;
                                                                 break;
                                                             case self::ID_CHAPTERDISPLAY:
                                                                 $chapterdisplay_entry = [];
                                                                 while ($this->getEBMLelement($sub_sub_sub_subelement, $sub_sub_subelement['end'], true)) {
                                                                     switch ($sub_sub_sub_subelement['id']) {
                                                                         case self::ID_CHAPSTRING:
                                                                         case self::ID_CHAPLANGUAGE:
                                                                         case self::ID_CHAPCOUNTRY:
                                                                             $chapterdisplay_entry[$sub_sub_sub_subelement['id_name']] = $sub_sub_sub_subelement['data'];
                                                                             break;
                                                                         default:
                                                                             $this->unhandledElement('chapters.editionentry.chapteratom.chapterdisplay', __LINE__, $sub_sub_sub_subelement);
                                                                     }
                                                                 }
                                                                 $chapteratom_entry[$sub_sub_subelement['id_name']][] = $chapterdisplay_entry;
                                                                 break;
                                                             default:
                                                                 $this->unhandledElement('chapters.editionentry.chapteratom', __LINE__, $sub_sub_subelement);
                                                         }
                                                     }
                                                     $editionentry_entry[$sub_subelement['id_name']][] = $chapteratom_entry;
                                                     break;
                                                 default:
                                                     $this->unhandledElement('chapters.editionentry', __LINE__, $sub_subelement);
                                             }
                                         }
                                         $info['matroska']['chapters'][] = $editionentry_entry;
                                         break;
                                     default:
                                         $this->unhandledElement('chapters', __LINE__, $subelement);
                                 }
                             }
                             break;
                         case self::ID_CLUSTER:
                             // The lower level element containing the (monolithic) Block structure.
                             $cluster_entry = [];
                             while ($this->getEBMLelement($subelement, $element_data['end'], [self::ID_CLUSTERSILENTTRACKS, self::ID_CLUSTERBLOCKGROUP, self::ID_CLUSTERSIMPLEBLOCK])) {
                                 switch ($subelement['id']) {
                                     case self::ID_CLUSTERTIMECODE:
                                     case self::ID_CLUSTERPOSITION:
                                     case self::ID_CLUSTERPREVSIZE:
                                         $cluster_entry[$subelement['id_name']] = Utils::BigEndian2Int($subelement['data']);
                                         break;
                                     case self::ID_CLUSTERSILENTTRACKS:
                                         $cluster_silent_tracks = [];
                                         while ($this->getEBMLelement($sub_subelement, $subelement['end'], true)) {
                                             switch ($sub_subelement['id']) {
                                                 case self::ID_CLUSTERSILENTTRACKNUMBER:
                                                     $cluster_silent_tracks[] = Utils::BigEndian2Int($sub_subelement['data']);
                                                     break;
                                                 default:
                                                     $this->unhandledElement('cluster.silenttracks', __LINE__, $sub_subelement);
                                             }
                                         }
                                         $cluster_entry[$subelement['id_name']][] = $cluster_silent_tracks;
                                         break;
                                     case self::ID_CLUSTERBLOCKGROUP:
                                         $cluster_block_group = ['offset' => $this->current_offset];
                                         while ($this->getEBMLelement($sub_subelement, $subelement['end'], [self::ID_CLUSTERBLOCK])) {
                                             switch ($sub_subelement['id']) {
                                                 case self::ID_CLUSTERBLOCK:
                                                     $cluster_block_group[$sub_subelement['id_name']] = $this->HandleEMBLClusterBlock($sub_subelement, self::ID_CLUSTERBLOCK, $info);
                                                     break;
                                                 case self::ID_CLUSTERREFERENCEPRIORITY:
                                                     // unsigned-int
                                                 // unsigned-int
                                                 case self::ID_CLUSTERBLOCKDURATION:
                                                     // unsigned-int
                                                     $cluster_block_group[$sub_subelement['id_name']] = Utils::BigEndian2Int($sub_subelement['data']);
                                                     break;
                                                 case self::ID_CLUSTERREFERENCEBLOCK:
                                                     // signed-int
                                                     $cluster_block_group[$sub_subelement['id_name']][] = Utils::BigEndian2Int($sub_subelement['data'], false, true);
                                                     break;
                                                 case self::ID_CLUSTERCODECSTATE:
                                                     $cluster_block_group[$sub_subelement['id_name']] = Utils::trimNullByte($sub_subelement['data']);
                                                     break;
                                                 default:
                                                     $this->unhandledElement('clusters.blockgroup', __LINE__, $sub_subelement);
                                             }
                                         }
                                         $cluster_entry[$subelement['id_name']][] = $cluster_block_group;
                                         break;
                                     case self::ID_CLUSTERSIMPLEBLOCK:
                                         $cluster_entry[$subelement['id_name']][] = $this->HandleEMBLClusterBlock($subelement, self::ID_CLUSTERSIMPLEBLOCK, $info);
                                         break;
                                     default:
                                         $this->unhandledElement('cluster', __LINE__, $subelement);
                                 }
                                 $this->current_offset = $subelement['end'];
                             }
                             if (!self::$hide_clusters) {
                                 $info['matroska']['cluster'][] = $cluster_entry;
                             }
                             // check to see if all the data we need exists already, if so, break out of the loop
                             if (!self::$parse_whole_file) {
                                 if (isset($info['matroska']['info']) && is_array($info['matroska']['info'])) {
                                     if (isset($info['matroska']['tracks']['tracks']) && is_array($info['matroska']['tracks']['tracks'])) {
                                         if (count($info['matroska']['track_data_offsets']) == count($info['matroska']['tracks']['tracks'])) {
                                             return;
                                         }
                                     }
                                 }
                             }
                             break;
                         default:
                             $this->unhandledElement('segment', __LINE__, $element_data);
                     }
                 }
                 break;
             default:
                 $this->unhandledElement('root', __LINE__, $top_element);
         }
     }
 }