wp/wp-includes/ID3/module.audio-video.matroska.php
changeset 16 a86126ab1dd4
parent 7 cf61fcea0001
child 19 3d72ae0968f4
equal deleted inserted replaced
15:3d4e9c994f10 16:a86126ab1dd4
     1 <?php
     1 <?php
       
     2 
     2 /////////////////////////////////////////////////////////////////
     3 /////////////////////////////////////////////////////////////////
     3 /// getID3() by James Heinrich <info@getid3.org>               //
     4 /// getID3() by James Heinrich <info@getid3.org>               //
     4 //  available at http://getid3.sourceforge.net                 //
     5 //  available at https://github.com/JamesHeinrich/getID3       //
     5 //            or http://www.getid3.org                         //
     6 //            or https://www.getid3.org                        //
     6 //          also https://github.com/JamesHeinrich/getID3       //
     7 //            or http://getid3.sourceforge.net                 //
     7 /////////////////////////////////////////////////////////////////
     8 //  see readme.txt for more details                            //
     8 // See readme.txt for more details                             //
       
     9 /////////////////////////////////////////////////////////////////
     9 /////////////////////////////////////////////////////////////////
    10 //                                                             //
    10 //                                                             //
    11 // module.audio-video.matriska.php                             //
    11 // module.audio-video.matriska.php                             //
    12 // module for analyzing Matroska containers                    //
    12 // module for analyzing Matroska containers                    //
    13 // dependencies: NONE                                          //
    13 // dependencies: NONE                                          //
    14 //                                                            ///
    14 //                                                            ///
    15 /////////////////////////////////////////////////////////////////
    15 /////////////////////////////////////////////////////////////////
    16 
    16 
       
    17 if (!defined('GETID3_INCLUDEPATH')) { // prevent path-exposing attacks that access modules directly on public webservers
       
    18 	exit;
       
    19 }
    17 
    20 
    18 define('EBML_ID_CHAPTERS',                  0x0043A770); // [10][43][A7][70] -- A system to define basic menus and partition data. For more detailed information, look at the Chapters Explanation.
    21 define('EBML_ID_CHAPTERS',                  0x0043A770); // [10][43][A7][70] -- A system to define basic menus and partition data. For more detailed information, look at the Chapters Explanation.
    19 define('EBML_ID_SEEKHEAD',                  0x014D9B74); // [11][4D][9B][74] -- Contains the position of other level 1 elements.
    22 define('EBML_ID_SEEKHEAD',                  0x014D9B74); // [11][4D][9B][74] -- Contains the position of other level 1 elements.
    20 define('EBML_ID_TAGS',                      0x0254C367); // [12][54][C3][67] -- Element containing elements specific to Tracks/Chapters. A list of valid tags can be found <http://www.matroska.org/technical/specs/tagging/index.html>.
    23 define('EBML_ID_TAGS',                      0x0254C367); // [12][54][C3][67] -- Element containing elements specific to Tracks/Chapters. A list of valid tags can be found <http://www.matroska.org/technical/specs/tagging/index.html>.
    21 define('EBML_ID_INFO',                      0x0549A966); // [15][49][A9][66] -- Contains miscellaneous general information and statistics on the file.
    24 define('EBML_ID_INFO',                      0x0549A966); // [15][49][A9][66] -- Contains miscellaneous general information and statistics on the file.
    70 define('EBML_ID_FILENAME',                      0x066E); //         [46][6E] -- Filename of the attached file.
    73 define('EBML_ID_FILENAME',                      0x066E); //         [46][6E] -- Filename of the attached file.
    71 define('EBML_ID_FILEREFERRAL',                  0x0675); //         [46][75] -- A binary value that a track/codec can refer to when the attachment is needed.
    74 define('EBML_ID_FILEREFERRAL',                  0x0675); //         [46][75] -- A binary value that a track/codec can refer to when the attachment is needed.
    72 define('EBML_ID_FILEDESCRIPTION',               0x067E); //         [46][7E] -- A human-friendly name for the attached file.
    75 define('EBML_ID_FILEDESCRIPTION',               0x067E); //         [46][7E] -- A human-friendly name for the attached file.
    73 define('EBML_ID_FILEUID',                       0x06AE); //         [46][AE] -- Unique ID representing the file, as random as possible.
    76 define('EBML_ID_FILEUID',                       0x06AE); //         [46][AE] -- Unique ID representing the file, as random as possible.
    74 define('EBML_ID_CONTENTENCALGO',                0x07E1); //         [47][E1] -- The encryption algorithm used. The value '0' means that the contents have not been encrypted but only signed. Predefined values:
    77 define('EBML_ID_CONTENTENCALGO',                0x07E1); //         [47][E1] -- The encryption algorithm used. The value '0' means that the contents have not been encrypted but only signed. Predefined values:
    75 define('EBML_ID_CONTENTENCKEYID',               0x07E2); //         [47][E2] -- For public key algorithms this is the ID of the public key the the data was encrypted with.
    78 define('EBML_ID_CONTENTENCKEYID',               0x07E2); //         [47][E2] -- For public key algorithms this is the ID of the public key the data was encrypted with.
    76 define('EBML_ID_CONTENTSIGNATURE',              0x07E3); //         [47][E3] -- A cryptographic signature of the contents.
    79 define('EBML_ID_CONTENTSIGNATURE',              0x07E3); //         [47][E3] -- A cryptographic signature of the contents.
    77 define('EBML_ID_CONTENTSIGKEYID',               0x07E4); //         [47][E4] -- This is the ID of the private key the data was signed with.
    80 define('EBML_ID_CONTENTSIGKEYID',               0x07E4); //         [47][E4] -- This is the ID of the private key the data was signed with.
    78 define('EBML_ID_CONTENTSIGALGO',                0x07E5); //         [47][E5] -- The algorithm used for the signature. A value of '0' means that the contents have not been signed but only encrypted. Predefined values:
    81 define('EBML_ID_CONTENTSIGALGO',                0x07E5); //         [47][E5] -- The algorithm used for the signature. A value of '0' means that the contents have not been signed but only encrypted. Predefined values:
    79 define('EBML_ID_CONTENTSIGHASHALGO',            0x07E6); //         [47][E6] -- The hash algorithm used for the signature. A value of '0' means that the contents have not been signed but only encrypted. Predefined values:
    82 define('EBML_ID_CONTENTSIGHASHALGO',            0x07E6); //         [47][E6] -- The hash algorithm used for the signature. A value of '0' means that the contents have not been signed but only encrypted. Predefined values:
    80 define('EBML_ID_MUXINGAPP',                     0x0D80); //         [4D][80] -- Muxing application or library ("libmatroska-0.4.3").
    83 define('EBML_ID_MUXINGAPP',                     0x0D80); //         [4D][80] -- Muxing application or library ("libmatroska-0.4.3").
   213 * @todo Rewrite EBML parser to reduce it's size and honor default element values
   216 * @todo Rewrite EBML parser to reduce it's size and honor default element values
   214 * @todo After rewrite implement stream size calculation, that will provide additional useful info and enable AAC/FLAC audio bitrate detection
   217 * @todo After rewrite implement stream size calculation, that will provide additional useful info and enable AAC/FLAC audio bitrate detection
   215 */
   218 */
   216 class getid3_matroska extends getid3_handler
   219 class getid3_matroska extends getid3_handler
   217 {
   220 {
   218 	// public options
   221 	/**
   219 	public static $hide_clusters    = true;  // if true, do not return information about CLUSTER chunks, since there's a lot of them and they're not usually useful [default: TRUE]
   222 	 * If true, do not return information about CLUSTER chunks, since there's a lot of them
   220 	public static $parse_whole_file = false; // true to parse the whole file, not only header [default: FALSE]
   223 	 * and they're not usually useful [default: TRUE].
   221 
   224 	 *
   222 	// private parser settings/placeholders
   225 	 * @var bool
       
   226 	 */
       
   227 	public static $hide_clusters    = true;
       
   228 
       
   229 	/**
       
   230 	 * True to parse the whole file, not only header [default: FALSE].
       
   231 	 *
       
   232 	 * @var bool
       
   233 	 */
       
   234 	public static $parse_whole_file = false;
       
   235 
       
   236 	/*
       
   237 	 * Private parser settings/placeholders.
       
   238 	 */
   223 	private $EBMLbuffer        = '';
   239 	private $EBMLbuffer        = '';
   224 	private $EBMLbuffer_offset = 0;
   240 	private $EBMLbuffer_offset = 0;
   225 	private $EBMLbuffer_length = 0;
   241 	private $EBMLbuffer_length = 0;
   226 	private $current_offset    = 0;
   242 	private $current_offset    = 0;
   227 	private $unuseful_elements = array(EBML_ID_CRC32, EBML_ID_VOID);
   243 	private $unuseful_elements = array(EBML_ID_CRC32, EBML_ID_VOID);
   228 
   244 
       
   245 	/**
       
   246 	 * @return bool
       
   247 	 */
   229 	public function Analyze()
   248 	public function Analyze()
   230 	{
   249 	{
   231 		$info = &$this->getid3->info;
   250 		$info = &$this->getid3->info;
   232 
   251 
   233 		// parse container
   252 		// parse container
   311 								}
   330 								}
   312 								$info['matroska']['track_codec_parsed'][$trackarray['TrackNumber']] = $h264;
   331 								$info['matroska']['track_codec_parsed'][$trackarray['TrackNumber']] = $h264;
   313 								break;*/
   332 								break;*/
   314 						}
   333 						}
   315 
   334 
   316 						$info['video']['streams'][] = $track_info;
   335 						$info['video']['streams'][$trackarray['TrackUID']] = $track_info;
   317 						break;
   336 						break;
   318 
   337 
   319 					case 2: // Audio
   338 					case 2: // Audio
   320 						$track_info['sample_rate'] = (isset($trackarray['SamplingFrequency']) ? $trackarray['SamplingFrequency'] : 8000.0);
   339 						$track_info['sample_rate'] = (isset($trackarray['SamplingFrequency']) ? $trackarray['SamplingFrequency'] : 8000.0);
   321 						$track_info['channels']    = (isset($trackarray['Channels']) ? $trackarray['Channels'] : 1);
   340 						$track_info['channels']    = (isset($trackarray['Channels']) ? $trackarray['Channels'] : 1);
   324 						if (isset($trackarray['CodecName'])) { $track_info['codec']           = $trackarray['CodecName']; }
   343 						if (isset($trackarray['CodecName'])) { $track_info['codec']           = $trackarray['CodecName']; }
   325 
   344 
   326 						switch ($trackarray['CodecID']) {
   345 						switch ($trackarray['CodecID']) {
   327 							case 'A_PCM/INT/LIT':
   346 							case 'A_PCM/INT/LIT':
   328 							case 'A_PCM/INT/BIG':
   347 							case 'A_PCM/INT/BIG':
   329 								$track_info['bitrate'] = $trackarray['SamplingFrequency'] * $trackarray['Channels'] * $trackarray['BitDepth'];
   348 								$track_info['bitrate'] = $track_info['sample_rate'] * $track_info['channels'] * $trackarray['BitDepth'];
   330 								break;
   349 								break;
   331 
   350 
   332 							case 'A_AC3':
   351 							case 'A_AC3':
   333 							case 'A_EAC3':
   352 							case 'A_EAC3':
   334 							case 'A_DTS':
   353 							case 'A_DTS':
   344 								}
   363 								}
   345 
   364 
   346 								// create temp instance
   365 								// create temp instance
   347 								$getid3_temp = new getID3();
   366 								$getid3_temp = new getID3();
   348 								if ($track_info['dataformat'] != 'flac') {
   367 								if ($track_info['dataformat'] != 'flac') {
   349 									$getid3_temp->openfile($this->getid3->filename);
   368 									$getid3_temp->openfile($this->getid3->filename, $this->getid3->info['filesize'], $this->getid3->fp);
   350 								}
   369 								}
   351 								$getid3_temp->info['avdataoffset'] = $info['matroska']['track_data_offsets'][$trackarray['TrackNumber']]['offset'];
   370 								$getid3_temp->info['avdataoffset'] = $info['matroska']['track_data_offsets'][$trackarray['TrackNumber']]['offset'];
   352 								if ($track_info['dataformat'][0] == 'm' || $track_info['dataformat'] == 'flac') {
   371 								if ($track_info['dataformat'][0] == 'm' || $track_info['dataformat'] == 'flac') {
   353 									$getid3_temp->info['avdataend'] = $info['matroska']['track_data_offsets'][$trackarray['TrackNumber']]['offset'] + $info['matroska']['track_data_offsets'][$trackarray['TrackNumber']]['length'];
   372 									$getid3_temp->info['avdataend'] = $info['matroska']['track_data_offsets'][$trackarray['TrackNumber']]['offset'] + $info['matroska']['track_data_offsets'][$trackarray['TrackNumber']]['length'];
   354 								}
   373 								}
   364 									$getid3_audio->Analyze();
   383 									$getid3_audio->Analyze();
   365 								}
   384 								}
   366 								if (!empty($getid3_temp->info[$header_data_key])) {
   385 								if (!empty($getid3_temp->info[$header_data_key])) {
   367 									$info['matroska']['track_codec_parsed'][$trackarray['TrackNumber']] = $getid3_temp->info[$header_data_key];
   386 									$info['matroska']['track_codec_parsed'][$trackarray['TrackNumber']] = $getid3_temp->info[$header_data_key];
   368 									if (isset($getid3_temp->info['audio']) && is_array($getid3_temp->info['audio'])) {
   387 									if (isset($getid3_temp->info['audio']) && is_array($getid3_temp->info['audio'])) {
   369 										foreach ($getid3_temp->info['audio'] as $key => $value) {
   388 										foreach ($getid3_temp->info['audio'] as $sub_key => $value) {
   370 											$track_info[$key] = $value;
   389 											$track_info[$sub_key] = $value;
   371 										}
   390 										}
   372 									}
   391 									}
   373 								}
   392 								}
   374 								else {
   393 								else {
   375 									$this->warning('Unable to parse audio data ['.basename(__FILE__).':'.__LINE__.'] because '.$class.'::Analyze() failed at offset '.$getid3_temp->info['avdataoffset']);
   394 									$this->warning('Unable to parse audio data ['.basename(__FILE__).':'.__LINE__.'] because '.$class.'::Analyze() failed at offset '.$getid3_temp->info['avdataoffset']);
   419 								$oggpageinfo['page_seqno'] = 0;
   438 								$oggpageinfo['page_seqno'] = 0;
   420 								$getid3_ogg->ParseVorbisPageHeader($trackarray['CodecPrivate'], $vorbis_offset, $oggpageinfo);
   439 								$getid3_ogg->ParseVorbisPageHeader($trackarray['CodecPrivate'], $vorbis_offset, $oggpageinfo);
   421 								if (!empty($getid3_temp->info['ogg'])) {
   440 								if (!empty($getid3_temp->info['ogg'])) {
   422 									$info['matroska']['track_codec_parsed'][$trackarray['TrackNumber']] = $getid3_temp->info['ogg'];
   441 									$info['matroska']['track_codec_parsed'][$trackarray['TrackNumber']] = $getid3_temp->info['ogg'];
   423 									if (isset($getid3_temp->info['audio']) && is_array($getid3_temp->info['audio'])) {
   442 									if (isset($getid3_temp->info['audio']) && is_array($getid3_temp->info['audio'])) {
   424 										foreach ($getid3_temp->info['audio'] as $key => $value) {
   443 										foreach ($getid3_temp->info['audio'] as $sub_key => $value) {
   425 											$track_info[$key] = $value;
   444 											$track_info[$sub_key] = $value;
   426 										}
   445 										}
   427 									}
   446 									}
   428 								}
   447 								}
   429 
   448 
   430 								// copy errors and warnings
   449 								// copy errors and warnings
   447 
   466 
   448 							case 'A_MS/ACM':
   467 							case 'A_MS/ACM':
   449 								getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio-video.riff.php', __FILE__, true);
   468 								getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio-video.riff.php', __FILE__, true);
   450 
   469 
   451 								$parsed = getid3_riff::parseWAVEFORMATex($trackarray['CodecPrivate']);
   470 								$parsed = getid3_riff::parseWAVEFORMATex($trackarray['CodecPrivate']);
   452 								foreach ($parsed as $key => $value) {
   471 								foreach ($parsed as $sub_key => $value) {
   453 									if ($key != 'raw') {
   472 									if ($sub_key != 'raw') {
   454 										$track_info[$key] = $value;
   473 										$track_info[$sub_key] = $value;
   455 									}
   474 									}
   456 								}
   475 								}
   457 								$info['matroska']['track_codec_parsed'][$trackarray['TrackNumber']] = $parsed;
   476 								$info['matroska']['track_codec_parsed'][$trackarray['TrackNumber']] = $parsed;
   458 								break;
   477 								break;
   459 
   478 
   460 							default:
   479 							default:
   461 								$this->warning('Unhandled audio type "'.(isset($trackarray['CodecID']) ? $trackarray['CodecID'] : '').'"');
   480 								$this->warning('Unhandled audio type "'.(isset($trackarray['CodecID']) ? $trackarray['CodecID'] : '').'"');
   462 								break;
   481 								break;
   463 						}
   482 						}
   464 
   483 
   465 						$info['audio']['streams'][] = $track_info;
   484 						$info['audio']['streams'][$trackarray['TrackUID']] = $track_info;
   466 						break;
   485 						break;
   467 				}
   486 				}
   468 			}
   487 			}
   469 
   488 
   470 			if (!empty($info['video']['streams'])) {
   489 			if (!empty($info['video']['streams'])) {
   491 			$info['mime_type'] = ($info['matroska']['doctype'] == 'webm' ? 'audio/webm' : 'audio/x-matroska');
   510 			$info['mime_type'] = ($info['matroska']['doctype'] == 'webm' ? 'audio/webm' : 'audio/x-matroska');
   492 		} elseif (isset($info['mime_type'])) {
   511 		} elseif (isset($info['mime_type'])) {
   493 			unset($info['mime_type']);
   512 			unset($info['mime_type']);
   494 		}
   513 		}
   495 
   514 
       
   515 		// use _STATISTICS_TAGS if available to set audio/video bitrates
       
   516 		if (!empty($info['matroska']['tags'])) {
       
   517 			$_STATISTICS_byTrackUID = array();
       
   518 			foreach ($info['matroska']['tags'] as $key1 => $value1) {
       
   519 				if (!empty($value1['Targets']['TagTrackUID'][0]) && !empty($value1['SimpleTag'])) {
       
   520 					foreach ($value1['SimpleTag'] as $key2 => $value2) {
       
   521 						if (!empty($value2['TagName']) && isset($value2['TagString'])) {
       
   522 							$_STATISTICS_byTrackUID[$value1['Targets']['TagTrackUID'][0]][$value2['TagName']] = $value2['TagString'];
       
   523 						}
       
   524 					}
       
   525 				}
       
   526 			}
       
   527 			foreach (array('audio','video') as $avtype) {
       
   528 				if (!empty($info[$avtype]['streams'])) {
       
   529 					foreach ($info[$avtype]['streams'] as $trackUID => $trackdata) {
       
   530 						if (!isset($trackdata['bitrate']) && !empty($_STATISTICS_byTrackUID[$trackUID]['BPS'])) {
       
   531 							$info[$avtype]['streams'][$trackUID]['bitrate'] = (int) $_STATISTICS_byTrackUID[$trackUID]['BPS'];
       
   532 							@$info[$avtype]['bitrate'] += $info[$avtype]['streams'][$trackUID]['bitrate'];
       
   533 						}
       
   534 					}
       
   535 				}
       
   536 			}
       
   537 		}
       
   538 
   496 		return true;
   539 		return true;
   497 	}
   540 	}
   498 
   541 
       
   542 	/**
       
   543 	 * @param array $info
       
   544 	 */
   499 	private function parseEBML(&$info) {
   545 	private function parseEBML(&$info) {
   500 		// http://www.matroska.org/technical/specs/index.html#EBMLBasics
   546 		// http://www.matroska.org/technical/specs/index.html#EBMLBasics
   501 		$this->current_offset = $info['avdataoffset'];
   547 		$this->current_offset = $info['avdataoffset'];
   502 
   548 
   503 		while ($this->getEBMLelement($top_element, $info['avdataend'])) {
   549 		while ($this->getEBMLelement($top_element, $info['avdataend'])) {
   593 										case EBML_ID_TRACKENTRY: //subelements: Describes a track with all elements.
   639 										case EBML_ID_TRACKENTRY: //subelements: Describes a track with all elements.
   594 
   640 
   595 											while ($this->getEBMLelement($subelement, $track_entry['end'], array(EBML_ID_VIDEO, EBML_ID_AUDIO, EBML_ID_CONTENTENCODINGS, EBML_ID_CODECPRIVATE))) {
   641 											while ($this->getEBMLelement($subelement, $track_entry['end'], array(EBML_ID_VIDEO, EBML_ID_AUDIO, EBML_ID_CONTENTENCODINGS, EBML_ID_CODECPRIVATE))) {
   596 												switch ($subelement['id']) {
   642 												switch ($subelement['id']) {
   597 
   643 
       
   644 													case EBML_ID_TRACKUID:
       
   645 														$track_entry[$subelement['id_name']] = getid3_lib::PrintHexBytes($subelement['data'], true, false);
       
   646 														break;
   598 													case EBML_ID_TRACKNUMBER:
   647 													case EBML_ID_TRACKNUMBER:
   599 													case EBML_ID_TRACKUID:
       
   600 													case EBML_ID_TRACKTYPE:
   648 													case EBML_ID_TRACKTYPE:
   601 													case EBML_ID_MINCACHE:
   649 													case EBML_ID_MINCACHE:
   602 													case EBML_ID_MAXCACHE:
   650 													case EBML_ID_MAXCACHE:
   603 													case EBML_ID_MAXBLOCKADDITIONID:
   651 													case EBML_ID_MAXBLOCKADDITIONID:
   604 													case EBML_ID_DEFAULTDURATION: // nanoseconds per frame
   652 													case EBML_ID_DEFAULTDURATION: // nanoseconds per frame
   942 
   990 
   943 																case EBML_ID_TAGTRACKUID:
   991 																case EBML_ID_TAGTRACKUID:
   944 																case EBML_ID_TAGEDITIONUID:
   992 																case EBML_ID_TAGEDITIONUID:
   945 																case EBML_ID_TAGCHAPTERUID:
   993 																case EBML_ID_TAGCHAPTERUID:
   946 																case EBML_ID_TAGATTACHMENTUID:
   994 																case EBML_ID_TAGATTACHMENTUID:
   947 																	$targets_entry[$sub_sub_subelement['id_name']][] = getid3_lib::BigEndian2Int($sub_sub_subelement['data']);
   995 																	$targets_entry[$sub_sub_subelement['id_name']][] = getid3_lib::PrintHexBytes($sub_sub_subelement['data'], true, false);
   948 																	break;
   996 																	break;
   949 
   997 
   950 																default:
   998 																default:
   951 																	$this->unhandledElement('tags.tag.targets', __LINE__, $sub_sub_subelement);
   999 																	$this->unhandledElement('tags.tag.targets', __LINE__, $sub_sub_subelement);
   952 																	break;
  1000 																	break;
  1226 					break;
  1274 					break;
  1227 			}
  1275 			}
  1228 		}
  1276 		}
  1229 	}
  1277 	}
  1230 
  1278 
       
  1279 	/**
       
  1280 	 * @param int $min_data
       
  1281 	 *
       
  1282 	 * @return bool
       
  1283 	 */
  1231 	private function EnsureBufferHasEnoughData($min_data=1024) {
  1284 	private function EnsureBufferHasEnoughData($min_data=1024) {
  1232 		if (($this->current_offset - $this->EBMLbuffer_offset) >= ($this->EBMLbuffer_length - $min_data)) {
  1285 		if (($this->current_offset - $this->EBMLbuffer_offset) >= ($this->EBMLbuffer_length - $min_data)) {
  1233 			$read_bytes = max($min_data, $this->getid3->fread_buffer_size());
  1286 			$read_bytes = max($min_data, $this->getid3->fread_buffer_size());
  1234 
  1287 
  1235 			try {
  1288 			try {
  1247 			}
  1300 			}
  1248 		}
  1301 		}
  1249 		return true;
  1302 		return true;
  1250 	}
  1303 	}
  1251 
  1304 
       
  1305 	/**
       
  1306 	 * @return int|float|false
       
  1307 	 */
  1252 	private function readEBMLint() {
  1308 	private function readEBMLint() {
  1253 		$actual_offset = $this->current_offset - $this->EBMLbuffer_offset;
  1309 		$actual_offset = $this->current_offset - $this->EBMLbuffer_offset;
  1254 
  1310 
  1255 		// get length of integer
  1311 		// get length of integer
  1256 		$first_byte_int = ord($this->EBMLbuffer[$actual_offset]);
  1312 		$first_byte_int = ord($this->EBMLbuffer[$actual_offset]);
  1279 		$this->current_offset += $length;
  1335 		$this->current_offset += $length;
  1280 
  1336 
  1281 		return $int_value;
  1337 		return $int_value;
  1282 	}
  1338 	}
  1283 
  1339 
       
  1340 	/**
       
  1341 	 * @param int  $length
       
  1342 	 * @param bool $check_buffer
       
  1343 	 *
       
  1344 	 * @return string|false
       
  1345 	 */
  1284 	private function readEBMLelementData($length, $check_buffer=false) {
  1346 	private function readEBMLelementData($length, $check_buffer=false) {
  1285 		if ($check_buffer && !$this->EnsureBufferHasEnoughData($length)) {
  1347 		if ($check_buffer && !$this->EnsureBufferHasEnoughData($length)) {
  1286 			return false;
  1348 			return false;
  1287 		}
  1349 		}
  1288 		$data = substr($this->EBMLbuffer, $this->current_offset - $this->EBMLbuffer_offset, $length);
  1350 		$data = substr($this->EBMLbuffer, $this->current_offset - $this->EBMLbuffer_offset, $length);
  1289 		$this->current_offset += $length;
  1351 		$this->current_offset += $length;
  1290 		return $data;
  1352 		return $data;
  1291 	}
  1353 	}
  1292 
  1354 
       
  1355 	/**
       
  1356 	 * @param array      $element
       
  1357 	 * @param int        $parent_end
       
  1358 	 * @param array|bool $get_data
       
  1359 	 *
       
  1360 	 * @return bool
       
  1361 	 */
  1293 	private function getEBMLelement(&$element, $parent_end, $get_data=false) {
  1362 	private function getEBMLelement(&$element, $parent_end, $get_data=false) {
  1294 		if ($this->current_offset >= $parent_end) {
  1363 		if ($this->current_offset >= $parent_end) {
  1295 			return false;
  1364 			return false;
  1296 		}
  1365 		}
  1297 
  1366 
  1324 		}
  1393 		}
  1325 
  1394 
  1326 		return true;
  1395 		return true;
  1327 	}
  1396 	}
  1328 
  1397 
       
  1398 	/**
       
  1399 	 * @param string $type
       
  1400 	 * @param int    $line
       
  1401 	 * @param array  $element
       
  1402 	 */
  1329 	private function unhandledElement($type, $line, $element) {
  1403 	private function unhandledElement($type, $line, $element) {
  1330 		// warn only about unknown and missed elements, not about unuseful
  1404 		// warn only about unknown and missed elements, not about unuseful
  1331 		if (!in_array($element['id'], $this->unuseful_elements)) {
  1405 		if (!in_array($element['id'], $this->unuseful_elements)) {
  1332 			$this->warning('Unhandled '.$type.' element ['.basename(__FILE__).':'.$line.'] ('.$element['id'].'::'.$element['id_name'].' ['.$element['length'].' bytes]) at '.$element['offset']);
  1406 			$this->warning('Unhandled '.$type.' element ['.basename(__FILE__).':'.$line.'] ('.$element['id'].'::'.$element['id_name'].' ['.$element['length'].' bytes]) at '.$element['offset']);
  1333 		}
  1407 		}
  1336 		if (!isset($element['data'])) {
  1410 		if (!isset($element['data'])) {
  1337 			$this->current_offset = $element['end'];
  1411 			$this->current_offset = $element['end'];
  1338 		}
  1412 		}
  1339 	}
  1413 	}
  1340 
  1414 
       
  1415 	/**
       
  1416 	 * @param array $SimpleTagArray
       
  1417 	 *
       
  1418 	 * @return bool
       
  1419 	 */
  1341 	private function ExtractCommentsSimpleTag($SimpleTagArray) {
  1420 	private function ExtractCommentsSimpleTag($SimpleTagArray) {
  1342 		if (!empty($SimpleTagArray['SimpleTag'])) {
  1421 		if (!empty($SimpleTagArray['SimpleTag'])) {
  1343 			foreach ($SimpleTagArray['SimpleTag'] as $SimpleTagKey => $SimpleTagData) {
  1422 			foreach ($SimpleTagArray['SimpleTag'] as $SimpleTagKey => $SimpleTagData) {
  1344 				if (!empty($SimpleTagData['TagName']) && !empty($SimpleTagData['TagString'])) {
  1423 				if (!empty($SimpleTagData['TagName']) && !empty($SimpleTagData['TagString'])) {
  1345 					$this->getid3->info['matroska']['comments'][strtolower($SimpleTagData['TagName'])][] = $SimpleTagData['TagString'];
  1424 					$this->getid3->info['matroska']['comments'][strtolower($SimpleTagData['TagName'])][] = $SimpleTagData['TagString'];
  1351 		}
  1430 		}
  1352 
  1431 
  1353 		return true;
  1432 		return true;
  1354 	}
  1433 	}
  1355 
  1434 
       
  1435 	/**
       
  1436 	 * @param int $parent_end
       
  1437 	 *
       
  1438 	 * @return array
       
  1439 	 */
  1356 	private function HandleEMBLSimpleTag($parent_end) {
  1440 	private function HandleEMBLSimpleTag($parent_end) {
  1357 		$simpletag_entry = array();
  1441 		$simpletag_entry = array();
  1358 
  1442 
  1359 		while ($this->getEBMLelement($element, $parent_end, array(EBML_ID_SIMPLETAG))) {
  1443 		while ($this->getEBMLelement($element, $parent_end, array(EBML_ID_SIMPLETAG))) {
  1360 			switch ($element['id']) {
  1444 			switch ($element['id']) {
  1381 		}
  1465 		}
  1382 
  1466 
  1383 		return $simpletag_entry;
  1467 		return $simpletag_entry;
  1384 	}
  1468 	}
  1385 
  1469 
       
  1470 	/**
       
  1471 	 * @param array $element
       
  1472 	 * @param int   $block_type
       
  1473 	 * @param array $info
       
  1474 	 *
       
  1475 	 * @return array
       
  1476 	 */
  1386 	private function HandleEMBLClusterBlock($element, $block_type, &$info) {
  1477 	private function HandleEMBLClusterBlock($element, $block_type, &$info) {
  1387 		// http://www.matroska.org/technical/specs/index.html#block_structure
  1478 		// http://www.matroska.org/technical/specs/index.html#block_structure
  1388 		// http://www.matroska.org/technical/specs/index.html#simpleblock_structure
  1479 		// http://www.matroska.org/technical/specs/index.html#simpleblock_structure
  1389 
  1480 
  1390 		$block_data = array();
  1481 		$block_data = array();
  1444 		$this->current_offset = $element['end'];
  1535 		$this->current_offset = $element['end'];
  1445 
  1536 
  1446 		return $block_data;
  1537 		return $block_data;
  1447 	}
  1538 	}
  1448 
  1539 
       
  1540 	/**
       
  1541 	 * @param string $EBMLstring
       
  1542 	 *
       
  1543 	 * @return int|float|false
       
  1544 	 */
  1449 	private static function EBML2Int($EBMLstring) {
  1545 	private static function EBML2Int($EBMLstring) {
  1450 		// http://matroska.org/specs/
  1546 		// http://matroska.org/specs/
  1451 
  1547 
  1452 		// Element ID coded with an UTF-8 like system:
  1548 		// Element ID coded with an UTF-8 like system:
  1453 		// 1xxx xxxx                                  - Class A IDs (2^7 -2 possible values) (base 0x8X)
  1549 		// 1xxx xxxx                                  - Class A IDs (2^7 -2 possible values) (base 0x8X)
  1486 		}
  1582 		}
  1487 
  1583 
  1488 		return getid3_lib::BigEndian2Int($EBMLstring);
  1584 		return getid3_lib::BigEndian2Int($EBMLstring);
  1489 	}
  1585 	}
  1490 
  1586 
       
  1587 	/**
       
  1588 	 * @param int $EBMLdatestamp
       
  1589 	 *
       
  1590 	 * @return float
       
  1591 	 */
  1491 	private static function EBMLdate2unix($EBMLdatestamp) {
  1592 	private static function EBMLdate2unix($EBMLdatestamp) {
  1492 		// Date - signed 8 octets integer in nanoseconds with 0 indicating the precise beginning of the millennium (at 2001-01-01T00:00:00,000000000 UTC)
  1593 		// Date - signed 8 octets integer in nanoseconds with 0 indicating the precise beginning of the millennium (at 2001-01-01T00:00:00,000000000 UTC)
  1493 		// 978307200 == mktime(0, 0, 0, 1, 1, 2001) == January 1, 2001 12:00:00am UTC
  1594 		// 978307200 == mktime(0, 0, 0, 1, 1, 2001) == January 1, 2001 12:00:00am UTC
  1494 		return round(($EBMLdatestamp / 1000000000) + 978307200);
  1595 		return round(($EBMLdatestamp / 1000000000) + 978307200);
  1495 	}
  1596 	}
  1496 
  1597 
       
  1598 	/**
       
  1599 	 * @param int $target_type
       
  1600 	 *
       
  1601 	 * @return string|int
       
  1602 	 */
  1497 	public static function TargetTypeValue($target_type) {
  1603 	public static function TargetTypeValue($target_type) {
  1498 		// http://www.matroska.org/technical/specs/tagging/index.html
  1604 		// http://www.matroska.org/technical/specs/tagging/index.html
  1499 		static $TargetTypeValue = array();
  1605 		static $TargetTypeValue = array();
  1500 		if (empty($TargetTypeValue)) {
  1606 		if (empty($TargetTypeValue)) {
  1501 			$TargetTypeValue[10] = 'A: ~ V:shot';                                           // the lowest hierarchy found in music or movies
  1607 			$TargetTypeValue[10] = 'A: ~ V:shot';                                           // the lowest hierarchy found in music or movies
  1507 			$TargetTypeValue[70] = 'A:collection ~ V:collection';                           // the high hierarchy consisting of many different lower items
  1613 			$TargetTypeValue[70] = 'A:collection ~ V:collection';                           // the high hierarchy consisting of many different lower items
  1508 		}
  1614 		}
  1509 		return (isset($TargetTypeValue[$target_type]) ? $TargetTypeValue[$target_type] : $target_type);
  1615 		return (isset($TargetTypeValue[$target_type]) ? $TargetTypeValue[$target_type] : $target_type);
  1510 	}
  1616 	}
  1511 
  1617 
       
  1618 	/**
       
  1619 	 * @param int $lacingtype
       
  1620 	 *
       
  1621 	 * @return string|int
       
  1622 	 */
  1512 	public static function BlockLacingType($lacingtype) {
  1623 	public static function BlockLacingType($lacingtype) {
  1513 		// http://matroska.org/technical/specs/index.html#block_structure
  1624 		// http://matroska.org/technical/specs/index.html#block_structure
  1514 		static $BlockLacingType = array();
  1625 		static $BlockLacingType = array();
  1515 		if (empty($BlockLacingType)) {
  1626 		if (empty($BlockLacingType)) {
  1516 			$BlockLacingType[0x00] = 'no lacing';
  1627 			$BlockLacingType[0x00] = 'no lacing';
  1519 			$BlockLacingType[0x03] = 'EBML lacing';
  1630 			$BlockLacingType[0x03] = 'EBML lacing';
  1520 		}
  1631 		}
  1521 		return (isset($BlockLacingType[$lacingtype]) ? $BlockLacingType[$lacingtype] : $lacingtype);
  1632 		return (isset($BlockLacingType[$lacingtype]) ? $BlockLacingType[$lacingtype] : $lacingtype);
  1522 	}
  1633 	}
  1523 
  1634 
       
  1635 	/**
       
  1636 	 * @param string $codecid
       
  1637 	 *
       
  1638 	 * @return string
       
  1639 	 */
  1524 	public static function CodecIDtoCommonName($codecid) {
  1640 	public static function CodecIDtoCommonName($codecid) {
  1525 		// http://www.matroska.org/technical/specs/codecid/index.html
  1641 		// http://www.matroska.org/technical/specs/codecid/index.html
  1526 		static $CodecIDlist = array();
  1642 		static $CodecIDlist = array();
  1527 		if (empty($CodecIDlist)) {
  1643 		if (empty($CodecIDlist)) {
  1528 			$CodecIDlist['A_AAC']            = 'aac';
  1644 			$CodecIDlist['A_AAC']            = 'aac';
  1555 			$CodecIDlist['A_MS/ACM']         = 'acm'; // Microsoft (TM) Audio Codec Manager (ACM)
  1671 			$CodecIDlist['A_MS/ACM']         = 'acm'; // Microsoft (TM) Audio Codec Manager (ACM)
  1556 		}
  1672 		}
  1557 		return (isset($CodecIDlist[$codecid]) ? $CodecIDlist[$codecid] : $codecid);
  1673 		return (isset($CodecIDlist[$codecid]) ? $CodecIDlist[$codecid] : $codecid);
  1558 	}
  1674 	}
  1559 
  1675 
       
  1676 	/**
       
  1677 	 * @param int $value
       
  1678 	 *
       
  1679 	 * @return string
       
  1680 	 */
  1560 	private static function EBMLidName($value) {
  1681 	private static function EBMLidName($value) {
  1561 		static $EBMLidList = array();
  1682 		static $EBMLidList = array();
  1562 		if (empty($EBMLidList)) {
  1683 		if (empty($EBMLidList)) {
  1563 			$EBMLidList[EBML_ID_ASPECTRATIOTYPE]            = 'AspectRatioType';
  1684 			$EBMLidList[EBML_ID_ASPECTRATIOTYPE]            = 'AspectRatioType';
  1564 			$EBMLidList[EBML_ID_ATTACHEDFILE]               = 'AttachedFile';
  1685 			$EBMLidList[EBML_ID_ATTACHEDFILE]               = 'AttachedFile';
  1753 		}
  1874 		}
  1754 
  1875 
  1755 		return (isset($EBMLidList[$value]) ? $EBMLidList[$value] : dechex($value));
  1876 		return (isset($EBMLidList[$value]) ? $EBMLidList[$value] : dechex($value));
  1756 	}
  1877 	}
  1757 
  1878 
       
  1879 	/**
       
  1880 	 * @param int $value
       
  1881 	 *
       
  1882 	 * @return string
       
  1883 	 */
  1758 	public static function displayUnit($value) {
  1884 	public static function displayUnit($value) {
  1759 		// http://www.matroska.org/technical/specs/index.html#DisplayUnit
  1885 		// http://www.matroska.org/technical/specs/index.html#DisplayUnit
  1760 		static $units = array(
  1886 		static $units = array(
  1761 			0 => 'pixels',
  1887 			0 => 'pixels',
  1762 			1 => 'centimeters',
  1888 			1 => 'centimeters',
  1764 			3 => 'Display Aspect Ratio');
  1890 			3 => 'Display Aspect Ratio');
  1765 
  1891 
  1766 		return (isset($units[$value]) ? $units[$value] : 'unknown');
  1892 		return (isset($units[$value]) ? $units[$value] : 'unknown');
  1767 	}
  1893 	}
  1768 
  1894 
       
  1895 	/**
       
  1896 	 * @param array $streams
       
  1897 	 *
       
  1898 	 * @return array
       
  1899 	 */
  1769 	private static function getDefaultStreamInfo($streams)
  1900 	private static function getDefaultStreamInfo($streams)
  1770 	{
  1901 	{
       
  1902 		$stream = array();
  1771 		foreach (array_reverse($streams) as $stream) {
  1903 		foreach (array_reverse($streams) as $stream) {
  1772 			if ($stream['default']) {
  1904 			if ($stream['default']) {
  1773 				break;
  1905 				break;
  1774 			}
  1906 			}
  1775 		}
  1907 		}