: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
$info['matroska']['comments']['picture'][] = array('data' => $entry['FileData'], 'image_mime' => $entry['FileMimeType'], 'filename' => $entry['FileName']);
if (!empty($info['video']['streams'])) {
$info['mime_type'] = ($info['matroska']['doctype'] == 'webm' ? 'video/webm' : 'video/x-matroska');
} elseif (!empty($info['audio']['streams'])) {
$info['mime_type'] = ($info['matroska']['doctype'] == 'webm' ? 'audio/webm' : 'audio/x-matroska');
} elseif (isset($info['mime_type'])) {
unset($info['mime_type']);
// use _STATISTICS_TAGS if available to set audio/video bitrates
if (!empty($info['matroska']['tags'])) {
$_STATISTICS_byTrackUID = array();
foreach ($info['matroska']['tags'] as $key1 => $value1) {
if (!empty($value1['Targets']['TagTrackUID'][0]) && !empty($value1['SimpleTag'])) {
foreach ($value1['SimpleTag'] as $key2 => $value2) {
if (!empty($value2['TagName']) && isset($value2['TagString'])) {
$_STATISTICS_byTrackUID[$value1['Targets']['TagTrackUID'][0]][$value2['TagName']] = $value2['TagString'];
foreach (array('audio','video') as $avtype) {
if (!empty($info[$avtype]['streams'])) {
foreach ($info[$avtype]['streams'] as $trackUID => $trackdata) {
if (!isset($trackdata['bitrate']) && !empty($_STATISTICS_byTrackUID[$trackUID]['BPS'])) {
$info[$avtype]['streams'][$trackUID]['bitrate'] = (int) $_STATISTICS_byTrackUID[$trackUID]['BPS'];
@$info[$avtype]['bitrate'] += $info[$avtype]['streams'][$trackUID]['bitrate'];
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']) {
$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 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($element_data['data']);
$element_data['data'] = getid3_lib::trimNullByte($element_data['data']);
$info['matroska']['doctype'] = $element_data['data'];
$info['fileformat'] = $element_data['data'];
$this->unhandledElement('header', __LINE__, $element_data);
unset($element_data['offset'], $element_data['end']);
$info['matroska']['header']['elements'][] = $element_data;
$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'] != EBML_ID_CLUSTER || !$this->hide_clusters) { // collect clusters only if required
$info['matroska']['segments'][] = $element_data;
switch ($element_data['id']) {
case EBML_ID_SEEKHEAD: // Contains the position of other level 1 elements.
while ($this->getEBMLelement($seek_entry, $element_data['end'])) {
switch ($seek_entry['id']) {
case EBML_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']) {
$seek_entry['target_id'] = self::EBML2Int($sub_seek_entry['data']);
$seek_entry['target_name'] = self::EBMLidName($seek_entry['target_id']);
case EBML_ID_SEEKPOSITION:
$seek_entry['target_offset'] = $element_data['offset'] + getid3_lib::BigEndian2Int($sub_seek_entry['data']);
$this->unhandledElement('seekhead.seek', __LINE__, $sub_seek_entry); }
if (!isset($seek_entry['target_id'])) {
$this->warning('seek_entry[target_id] unexpectedly not set at '.$seek_entry['offset']);
if (($seek_entry['target_id'] != EBML_ID_CLUSTER) || !$this->hide_clusters) { // collect clusters only if required
$info['matroska']['seek'][] = $seek_entry;
$this->unhandledElement('seekhead', __LINE__, $seek_entry);
case EBML_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 EBML_ID_TRACKENTRY: //subelements: Describes a track with all elements.
while ($this->getEBMLelement($subelement, $track_entry['end'], array(EBML_ID_VIDEO, EBML_ID_AUDIO, EBML_ID_CONTENTENCODINGS, EBML_ID_CODECPRIVATE))) {
switch ($subelement['id']) {
$track_entry[$subelement['id_name']] = getid3_lib::PrintHexBytes($subelement['data'], true, false);
case EBML_ID_TRACKNUMBER:
case EBML_ID_MAXBLOCKADDITIONID:
case EBML_ID_DEFAULTDURATION: // nanoseconds per frame
$track_entry[$subelement['id_name']] = getid3_lib::BigEndian2Int($subelement['data']);
case EBML_ID_TRACKTIMECODESCALE:
$track_entry[$subelement['id_name']] = getid3_lib::BigEndian2Float($subelement['data']);
$track_entry[$subelement['id_name']] = getid3_lib::trimNullByte($subelement['data']);
case EBML_ID_CODECPRIVATE:
$track_entry[$subelement['id_name']] = $this->readEBMLelementData($subelement['length'], true);
case EBML_ID_FLAGENABLED:
case EBML_ID_FLAGDEFAULT:
case EBML_ID_CODECDECODEALL:
$track_entry[$subelement['id_name']] = (bool) getid3_lib::BigEndian2Int($subelement['data']);
while ($this->getEBMLelement($sub_subelement, $subelement['end'], true)) {
switch ($sub_subelement['id']) {
case EBML_ID_PIXELHEIGHT:
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:
case EBML_ID_OLDSTEREOMODE:
$track_entry[$sub_subelement['id_name']] = getid3_lib::BigEndian2Int($sub_subelement['data']);
case EBML_ID_FLAGINTERLACED:
$track_entry[$sub_subelement['id_name']] = (bool)getid3_lib::BigEndian2Int($sub_subelement['data']);
$track_entry[$sub_subelement['id_name']] = getid3_lib::BigEndian2Float($sub_subelement['data']);
case EBML_ID_COLOURSPACE:
$track_entry[$sub_subelement['id_name']] = getid3_lib::trimNullByte($sub_subelement['data']);
$this->unhandledElement('track.video', __LINE__, $sub_subelement);
while ($this->getEBMLelement($sub_subelement, $subelement['end'], true)) {
switch ($sub_subelement['id']) {
$track_entry[$sub_subelement['id_name']] = getid3_lib::BigEndian2Int($sub_subelement['data']);
case EBML_ID_SAMPLINGFREQUENCY:
case EBML_ID_OUTPUTSAMPLINGFREQUENCY:
$track_entry[$sub_subelement['id_name']] = getid3_lib::BigEndian2Float($sub_subelement['data']);
case EBML_ID_CHANNELPOSITIONS:
$track_entry[$sub_subelement['id_name']] = getid3_lib::trimNullByte($sub_subelement['data']);
$this->unhandledElement('track.audio', __LINE__, $sub_subelement);
case EBML_ID_CONTENTENCODINGS:
while ($this->getEBMLelement($sub_subelement, $subelement['end'])) {
switch ($sub_subelement['id']) {
case EBML_ID_CONTENTENCODING:
while ($this->getEBMLelement($sub_sub_subelement, $sub_subelement['end'], array(EBML_ID_CONTENTCOMPRESSION, EBML_ID_CONTENTENCRYPTION))) {
switch ($sub_sub_subelement['id']) {
case EBML_ID_CONTENTENCODINGORDER:
case EBML_ID_CONTENTENCODINGSCOPE:
case EBML_ID_CONTENTENCODINGTYPE:
$track_entry[$sub_subelement['id_name']][$sub_sub_subelement['id_name']] = getid3_lib::BigEndian2Int($sub_sub_subelement['data']);
case EBML_ID_CONTENTCOMPRESSION:
while ($this->getEBMLelement($sub_sub_sub_subelement, $sub_sub_subelement['end'], true)) {
switch ($sub_sub_sub_subelement['id']) {
case EBML_ID_CONTENTCOMPALGO:
$track_entry[$sub_subelement['id_name']][$sub_sub_subelement['id_name']][$sub_sub_sub_subelement['id_name']] = getid3_lib::BigEndian2Int($sub_sub_sub_subelement['data']);
case EBML_ID_CONTENTCOMPSETTINGS:
$track_entry[$sub_subelement['id_name']][$sub_sub_subelement['id_name']][$sub_sub_sub_subelement['id_name']] = $sub_sub_sub_subelement['data'];
$this->unhandledElement('track.contentencodings.contentencoding.contentcompression', __LINE__, $sub_sub_sub_subelement);
case EBML_ID_CONTENTENCRYPTION:
while ($this->getEBMLelement($sub_sub_sub_subelement, $sub_sub_subelement['end'], true)) {
switch ($sub_sub_sub_subelement['id']) {
case EBML_ID_CONTENTENCALGO:
case EBML_ID_CONTENTSIGALGO:
case EBML_ID_CONTENTSIGHASHALGO:
$track_entry[$sub_subelement['id_name']][$sub_sub_subelement['id_name']][$sub_sub_sub_subelement['id_name']] = getid3_lib::BigEndian2Int($sub_sub_sub_subelement['data']);
case EBML_ID_CONTENTENCKEYID:
case EBML_ID_CONTENTSIGNATURE:
case EBML_ID_CONTENTSIGKEYID:
$track_entry[$sub_subelement['id_name']][$sub_sub_subelement['id_name']][$sub_sub_sub_subelement['id_name']] = $sub_sub_sub_subelement['data'];
$this->unhandledElement('track.contentencodings.contentencoding.contentcompression', __LINE__, $sub_sub_sub_subelement);
$this->unhandledElement('track.contentencodings.contentencoding', __LINE__, $sub_sub_subelement);
$this->unhandledElement('track.contentencodings', __LINE__, $sub_subelement);
$this->unhandledElement('track', __LINE__, $subelement);
$info['matroska']['tracks']['tracks'][] = $track_entry;
$this->unhandledElement('tracks', __LINE__, $track_entry);
case EBML_ID_INFO: // Contains miscellaneous general information and statistics on the file.
while ($this->getEBMLelement($subelement, $element_data['end'], true)) {
switch ($subelement['id']) {
case EBML_ID_TIMECODESCALE:
$info_entry[$subelement['id_name']] = getid3_lib::BigEndian2Int($subelement['data']);
$info_entry[$subelement['id_name']] = getid3_lib::BigEndian2Float($subelement['data']);
$info_entry[$subelement['id_name']] = getid3_lib::BigEndian2Int($subelement['data']);
$info_entry[$subelement['id_name'].'_unix'] = self::EBMLdate2unix($info_entry[$subelement['id_name']]);
$info_entry[$subelement['id_name']] = getid3_lib::trimNullByte($subelement['data']);
case EBML_ID_SEGMENTFAMILY:
$info_entry[$subelement['id_name']][] = getid3_lib::trimNullByte($subelement['data']);
case EBML_ID_SEGMENTFILENAME:
case EBML_ID_PREVFILENAME:
case EBML_ID_NEXTFILENAME:
$info_entry[$subelement['id_name']] = getid3_lib::trimNullByte($subelement['data']);
$info['matroska']['comments'][strtolower($subelement['id_name'])][] = $info_entry[$subelement['id_name']];
case EBML_ID_CHAPTERTRANSLATE:
$chaptertranslate_entry = array();
while ($this->getEBMLelement($sub_subelement, $subelement['end'], true)) {
switch ($sub_subelement['id']) {
case EBML_ID_CHAPTERTRANSLATEEDITIONUID:
$chaptertranslate_entry[$sub_subelement['id_name']][] = getid3_lib::BigEndian2Int($sub_subelement['data']);
case EBML_ID_CHAPTERTRANSLATECODEC:
$chaptertranslate_entry[$sub_subelement['id_name']] = getid3_lib::BigEndian2Int($sub_subelement['data']);
case EBML_ID_CHAPTERTRANSLATEID:
$chaptertranslate_entry[$sub_subelement['id_name']] = getid3_lib::trimNullByte($sub_subelement['data']);
$this->unhandledElement('info.chaptertranslate', __LINE__, $sub_subelement);
$info_entry[$subelement['id_name']] = $chaptertranslate_entry;
$this->unhandledElement('info', __LINE__, $subelement);
$info['matroska']['info'][] = $info_entry;
case EBML_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 ($this->hide_clusters) { // do not parse cues if hide clusters is "ON" till they point to clusters anyway
$this->current_offset = $element_data['end'];
while ($this->getEBMLelement($subelement, $element_data['end'])) {
switch ($subelement['id']) {
$cuepoint_entry = array();
while ($this->getEBMLelement($sub_subelement, $subelement['end'], array(EBML_ID_CUETRACKPOSITIONS))) {
switch ($sub_subelement['id']) {
case EBML_ID_CUETRACKPOSITIONS:
$cuetrackpositions_entry = array();
while ($this->getEBMLelement($sub_sub_subelement, $sub_subelement['end'], true)) {
switch ($sub_sub_subelement['id']) {
case EBML_ID_CUECLUSTERPOSITION:
case EBML_ID_CUEBLOCKNUMBER:
case EBML_ID_CUECODECSTATE:
$cuetrackpositions_entry[$sub_sub_subelement['id_name']] = getid3_lib::BigEndian2Int($sub_sub_subelement['data']);
$this->unhandledElement('cues.cuepoint.cuetrackpositions', __LINE__, $sub_sub_subelement);
$cuepoint_entry[$sub_subelement['id_name']][] = $cuetrackpositions_entry;
$cuepoint_entry[$sub_subelement['id_name']] = getid3_lib::BigEndian2Int($sub_subelement['data']);
$this->unhandledElement('cues.cuepoint', __LINE__, $sub_subelement);
$cues_entry[] = $cuepoint_entry;
$this->unhandledElement('cues', __LINE__, $subelement);
$info['matroska']['cues'] = $cues_entry;
case EBML_ID_TAGS: // Element containing elements specific to Tracks/Chapters.
while ($this->getEBMLelement($subelement, $element_data['end'], false)) {
switch ($subelement['id']) {
while ($this->getEBMLelement($sub_subelement, $subelement['end'], false)) {
switch ($sub_subelement['id']) {
$targets_entry = array();
while ($this->getEBMLelement($sub_sub_subelement, $sub_subelement['end'], true)) {
switch ($sub_sub_subelement['id']) {
case EBML_ID_TARGETTYPEVALUE:
$targets_entry[$sub_sub_subelement['id_name']] = getid3_lib::BigEndian2Int($sub_sub_subelement['data']);
$targets_entry[strtolower($sub_sub_subelement['id_name']).'_long'] = self::TargetTypeValue($targets_entry[$sub_sub_subelement['id_name']]);
$targets_entry[$sub_sub_subelement['id_name']] = $sub_sub_subelement['data'];
case EBML_ID_TAGTRACKUID:
case EBML_ID_TAGEDITIONUID:
case EBML_ID_TAGCHAPTERUID:
case EBML_ID_TAGATTACHMENTUID:
$targets_entry[$sub_sub_subelement['id_name']][] = getid3_lib::PrintHexBytes($sub_sub_subelement['data'], true, false);
$this->unhandledElement('tags.tag.targets', __LINE__, $sub_sub_subelement);