: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
$tag_entry[$sub_subelement['id_name']] = $targets_entry;
$tag_entry[$sub_subelement['id_name']][] = $this->HandleEMBLSimpleTag($sub_subelement['end']);
$this->unhandledElement('tags.tag', __LINE__, $sub_subelement);
$tags_entry[] = $tag_entry;
$this->unhandledElement('tags', __LINE__, $subelement);
$info['matroska']['tags'] = $tags_entry;
case EBML_ID_ATTACHMENTS: // Contain attached files.
while ($this->getEBMLelement($subelement, $element_data['end'])) {
switch ($subelement['id']) {
case EBML_ID_ATTACHEDFILE:
$attachedfile_entry = array();
while ($this->getEBMLelement($sub_subelement, $subelement['end'], array(EBML_ID_FILEDATA))) {
switch ($sub_subelement['id']) {
case EBML_ID_FILEDESCRIPTION:
case EBML_ID_FILEMIMETYPE:
$attachedfile_entry[$sub_subelement['id_name']] = $sub_subelement['data'];
$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'];
$attachedfile_entry[$sub_subelement['id_name']] = getid3_lib::BigEndian2Int($sub_subelement['data']);
$this->unhandledElement('attachments.attachedfile', __LINE__, $sub_subelement);
$info['matroska']['attachments'][] = $attachedfile_entry;
$this->unhandledElement('attachments', __LINE__, $subelement);
while ($this->getEBMLelement($subelement, $element_data['end'])) {
switch ($subelement['id']) {
case EBML_ID_EDITIONENTRY:
$editionentry_entry = array();
while ($this->getEBMLelement($sub_subelement, $subelement['end'], array(EBML_ID_CHAPTERATOM))) {
switch ($sub_subelement['id']) {
$editionentry_entry[$sub_subelement['id_name']] = getid3_lib::BigEndian2Int($sub_subelement['data']);
case EBML_ID_EDITIONFLAGHIDDEN:
case EBML_ID_EDITIONFLAGDEFAULT:
case EBML_ID_EDITIONFLAGORDERED:
$editionentry_entry[$sub_subelement['id_name']] = (bool)getid3_lib::BigEndian2Int($sub_subelement['data']);
case EBML_ID_CHAPTERATOM:
$chapteratom_entry = array();
while ($this->getEBMLelement($sub_sub_subelement, $sub_subelement['end'], array(EBML_ID_CHAPTERTRACK, EBML_ID_CHAPTERDISPLAY))) {
switch ($sub_sub_subelement['id']) {
case EBML_ID_CHAPTERSEGMENTUID:
case EBML_ID_CHAPTERSEGMENTEDITIONUID:
$chapteratom_entry[$sub_sub_subelement['id_name']] = $sub_sub_subelement['data'];
case EBML_ID_CHAPTERFLAGENABLED:
case EBML_ID_CHAPTERFLAGHIDDEN:
$chapteratom_entry[$sub_sub_subelement['id_name']] = (bool)getid3_lib::BigEndian2Int($sub_sub_subelement['data']);
case EBML_ID_CHAPTERTIMESTART:
case EBML_ID_CHAPTERTIMEEND:
$chapteratom_entry[$sub_sub_subelement['id_name']] = getid3_lib::BigEndian2Int($sub_sub_subelement['data']);
case EBML_ID_CHAPTERTRACK:
$chaptertrack_entry = array();
while ($this->getEBMLelement($sub_sub_sub_subelement, $sub_sub_subelement['end'], true)) {
switch ($sub_sub_sub_subelement['id']) {
case EBML_ID_CHAPTERTRACKNUMBER:
$chaptertrack_entry[$sub_sub_sub_subelement['id_name']] = getid3_lib::BigEndian2Int($sub_sub_sub_subelement['data']);
$this->unhandledElement('chapters.editionentry.chapteratom.chaptertrack', __LINE__, $sub_sub_sub_subelement);
$chapteratom_entry[$sub_sub_subelement['id_name']][] = $chaptertrack_entry;
case EBML_ID_CHAPTERDISPLAY:
$chapterdisplay_entry = array();
while ($this->getEBMLelement($sub_sub_sub_subelement, $sub_sub_subelement['end'], true)) {
switch ($sub_sub_sub_subelement['id']) {
case EBML_ID_CHAPLANGUAGE:
case EBML_ID_CHAPCOUNTRY:
$chapterdisplay_entry[$sub_sub_sub_subelement['id_name']] = $sub_sub_sub_subelement['data'];
$this->unhandledElement('chapters.editionentry.chapteratom.chapterdisplay', __LINE__, $sub_sub_sub_subelement);
$chapteratom_entry[$sub_sub_subelement['id_name']][] = $chapterdisplay_entry;
$this->unhandledElement('chapters.editionentry.chapteratom', __LINE__, $sub_sub_subelement);
$editionentry_entry[$sub_subelement['id_name']][] = $chapteratom_entry;
$this->unhandledElement('chapters.editionentry', __LINE__, $sub_subelement);
$info['matroska']['chapters'][] = $editionentry_entry;
$this->unhandledElement('chapters', __LINE__, $subelement);
case EBML_ID_CLUSTER: // The lower level element containing the (monolithic) Block structure.
$cluster_entry = array();
while ($this->getEBMLelement($subelement, $element_data['end'], array(EBML_ID_CLUSTERSILENTTRACKS, EBML_ID_CLUSTERBLOCKGROUP, EBML_ID_CLUSTERSIMPLEBLOCK))) {
switch ($subelement['id']) {
case EBML_ID_CLUSTERTIMECODE:
case EBML_ID_CLUSTERPOSITION:
case EBML_ID_CLUSTERPREVSIZE:
$cluster_entry[$subelement['id_name']] = getid3_lib::BigEndian2Int($subelement['data']);
case EBML_ID_CLUSTERSILENTTRACKS:
$cluster_silent_tracks = array();
while ($this->getEBMLelement($sub_subelement, $subelement['end'], true)) {
switch ($sub_subelement['id']) {
case EBML_ID_CLUSTERSILENTTRACKNUMBER:
$cluster_silent_tracks[] = getid3_lib::BigEndian2Int($sub_subelement['data']);
$this->unhandledElement('cluster.silenttracks', __LINE__, $sub_subelement);
$cluster_entry[$subelement['id_name']][] = $cluster_silent_tracks;
case EBML_ID_CLUSTERBLOCKGROUP:
$cluster_block_group = array('offset' => $this->current_offset);
while ($this->getEBMLelement($sub_subelement, $subelement['end'], array(EBML_ID_CLUSTERBLOCK))) {
switch ($sub_subelement['id']) {
case EBML_ID_CLUSTERBLOCK:
$cluster_block_group[$sub_subelement['id_name']] = $this->HandleEMBLClusterBlock($sub_subelement, EBML_ID_CLUSTERBLOCK, $info);
case EBML_ID_CLUSTERREFERENCEPRIORITY: // unsigned-int
case EBML_ID_CLUSTERBLOCKDURATION: // unsigned-int
$cluster_block_group[$sub_subelement['id_name']] = getid3_lib::BigEndian2Int($sub_subelement['data']);
case EBML_ID_CLUSTERREFERENCEBLOCK: // signed-int
$cluster_block_group[$sub_subelement['id_name']][] = getid3_lib::BigEndian2Int($sub_subelement['data'], false, true);
case EBML_ID_CLUSTERCODECSTATE:
$cluster_block_group[$sub_subelement['id_name']] = getid3_lib::trimNullByte($sub_subelement['data']);
$this->unhandledElement('clusters.blockgroup', __LINE__, $sub_subelement);
$cluster_entry[$subelement['id_name']][] = $cluster_block_group;
case EBML_ID_CLUSTERSIMPLEBLOCK:
$cluster_entry[$subelement['id_name']][] = $this->HandleEMBLClusterBlock($subelement, EBML_ID_CLUSTERSIMPLEBLOCK, $info);
$this->unhandledElement('cluster', __LINE__, $subelement);
$this->current_offset = $subelement['end'];
if (!$this->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 (!$this->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'])) {
$this->unhandledElement('segment', __LINE__, $element_data);
$this->unhandledElement('root', __LINE__, $top_element);
private function EnsureBufferHasEnoughData($min_data=1024) {
if (($this->current_offset - $this->EBMLbuffer_offset) >= ($this->EBMLbuffer_length - $min_data)) {
$read_bytes = max($min_data, $this->getid3->fread_buffer_size());
$this->fseek($this->current_offset);
$this->EBMLbuffer_offset = $this->current_offset;
$this->EBMLbuffer = $this->fread($read_bytes);
$this->EBMLbuffer_length = strlen($this->EBMLbuffer);
} catch (getid3_exception $e) {
$this->warning('EBML parser: '.$e->getMessage());
if ($this->EBMLbuffer_length == 0 && $this->feof()) {
return $this->error('EBML parser: ran out of file at offset '.$this->current_offset);
* @return int|float|false
private function readEBMLint() {
$actual_offset = $this->current_offset - $this->EBMLbuffer_offset;
$first_byte_int = ord($this->EBMLbuffer[$actual_offset]);
if (0x80 & $first_byte_int) {
} elseif (0x40 & $first_byte_int) {
} elseif (0x20 & $first_byte_int) {
} elseif (0x10 & $first_byte_int) {
} elseif (0x08 & $first_byte_int) {
} elseif (0x04 & $first_byte_int) {
} elseif (0x02 & $first_byte_int) {
} elseif (0x01 & $first_byte_int) {
throw new Exception('invalid EBML integer (leading 0x00) at '.$this->current_offset);
$int_value = self::EBML2Int(substr($this->EBMLbuffer, $actual_offset, $length));
$this->current_offset += $length;
* @param bool $check_buffer
private function readEBMLelementData($length, $check_buffer=false) {
if ($check_buffer && !$this->EnsureBufferHasEnoughData($length)) {
$data = substr($this->EBMLbuffer, $this->current_offset - $this->EBMLbuffer_offset, $length);
$this->current_offset += $length;
* @param array|bool $get_data
private function getEBMLelement(&$element, $parent_end, $get_data=false) {
if ($this->current_offset >= $parent_end) {
if (!$this->EnsureBufferHasEnoughData()) {
$this->current_offset = PHP_INT_MAX; // do not exit parser right now, allow to finish current loop to gather maximum information
$element['offset'] = $this->current_offset;
$element['id'] = $this->readEBMLint();
$element['id_name'] = self::EBMLidName($element['id']);
$element['length'] = $this->readEBMLint();
$element['end'] = $this->current_offset + $element['length'];
$dont_parse = (in_array($element['id'], $this->unuseful_elements) || $element['id_name'] == dechex($element['id']));
if (($get_data === true || (is_array($get_data) && !in_array($element['id'], $get_data))) && !$dont_parse) {
$element['data'] = $this->readEBMLelementData($element['length'], $element);
private function unhandledElement($type, $line, $element) {
// warn only about unknown and missed elements, not about unuseful
if (!in_array($element['id'], $this->unuseful_elements)) {
$this->warning('Unhandled '.$type.' element ['.basename(__FILE__).':'.$line.'] ('.$element['id'].'::'.$element['id_name'].' ['.$element['length'].' bytes]) at '.$element['offset']);
// increase offset for unparsed elements
if (!isset($element['data'])) {
$this->current_offset = $element['end'];
* @param array $SimpleTagArray
private function ExtractCommentsSimpleTag($SimpleTagArray) {
if (!empty($SimpleTagArray['SimpleTag'])) {
foreach ($SimpleTagArray['SimpleTag'] as $SimpleTagKey => $SimpleTagData) {
if (!empty($SimpleTagData['TagName']) && !empty($SimpleTagData['TagString'])) {
$this->getid3->info['matroska']['comments'][strtolower($SimpleTagData['TagName'])][] = $SimpleTagData['TagString'];
if (!empty($SimpleTagData['SimpleTag'])) {
$this->ExtractCommentsSimpleTag($SimpleTagData);
private function HandleEMBLSimpleTag($parent_end) {
$simpletag_entry = array();
while ($this->getEBMLelement($element, $parent_end, array(EBML_ID_SIMPLETAG))) {
switch ($element['id']) {
case EBML_ID_TAGLANGUAGE:
$simpletag_entry[$element['id_name']] = $element['data'];
$simpletag_entry[$element['id_name']][] = $this->HandleEMBLSimpleTag($element['end']);
$simpletag_entry[$element['id_name']] = (bool)getid3_lib::BigEndian2Int($element['data']);
$this->unhandledElement('tag.simpletag', __LINE__, $element);
private function HandleEMBLClusterBlock($element, $block_type, &$info) {
// http://www.matroska.org/technical/specs/index.html#block_structure
// http://www.matroska.org/technical/specs/index.html#simpleblock_structure
$block_data['tracknumber'] = $this->readEBMLint();
$block_data['timecode'] = getid3_lib::BigEndian2Int($this->readEBMLelementData(2), false, true);
$block_data['flags_raw'] = getid3_lib::BigEndian2Int($this->readEBMLelementData(1));
if ($block_type == EBML_ID_CLUSTERSIMPLEBLOCK) {
$block_data['flags']['keyframe'] = (($block_data['flags_raw'] & 0x80) >> 7);
//$block_data['flags']['reserved1'] = (($block_data['flags_raw'] & 0x70) >> 4);
//$block_data['flags']['reserved1'] = (($block_data['flags_raw'] & 0xF0) >> 4);
$block_data['flags']['invisible'] = (bool)(($block_data['flags_raw'] & 0x08) >> 3);
$block_data['flags']['lacing'] = (($block_data['flags_raw'] & 0x06) >> 1); // 00=no lacing; 01=Xiph lacing; 11=EBML lacing; 10=fixed-size lacing
if ($block_type == EBML_ID_CLUSTERSIMPLEBLOCK) {
$block_data['flags']['discardable'] = (($block_data['flags_raw'] & 0x01));
//$block_data['flags']['reserved2'] = (($block_data['flags_raw'] & 0x01) >> 0);