getid3_matroska

The WordPress Core getid3 matroska class.

Defined (1)

The class is defined in the following location(s).

/wp-includes/ID3/module.audio-video.matroska.php  
  1. class getid3_matroska extends getid3_handler 
  2. // public options 
  3. 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] 
  4. public static $parse_whole_file = false; // true to parse the whole file, not only header [default: FALSE] 
  5.  
  6. // private parser settings/placeholders 
  7. private $EBMLbuffer = ''; 
  8. private $EBMLbuffer_offset = 0; 
  9. private $EBMLbuffer_length = 0; 
  10. private $current_offset = 0; 
  11. private $unuseful_elements = array(EBML_ID_CRC32, EBML_ID_VOID); 
  12.  
  13. public function Analyze() 
  14. $info = &$this->getid3->info; 
  15.  
  16. // parse container 
  17. try { 
  18. $this->parseEBML($info); 
  19. } catch (Exception $e) { 
  20. $info['error'][] = 'EBML parser: '.$e->getMessage(); 
  21.  
  22. // calculate playtime 
  23. if (isset($info['matroska']['info']) && is_array($info['matroska']['info'])) { 
  24. foreach ($info['matroska']['info'] as $key => $infoarray) { 
  25. if (isset($infoarray['Duration'])) { 
  26. // TimecodeScale is how many nanoseconds each Duration unit is 
  27. $info['playtime_seconds'] = $infoarray['Duration'] * ((isset($infoarray['TimecodeScale']) ? $infoarray['TimecodeScale'] : 1000000) / 1000000000); 
  28. break; 
  29.  
  30. // extract tags 
  31. if (isset($info['matroska']['tags']) && is_array($info['matroska']['tags'])) { 
  32. foreach ($info['matroska']['tags'] as $key => $infoarray) { 
  33. $this->ExtractCommentsSimpleTag($infoarray); 
  34.  
  35. // process tracks 
  36. if (isset($info['matroska']['tracks']['tracks']) && is_array($info['matroska']['tracks']['tracks'])) { 
  37. foreach ($info['matroska']['tracks']['tracks'] as $key => $trackarray) { 
  38.  
  39. $track_info = array(); 
  40. $track_info['dataformat'] = self::CodecIDtoCommonName($trackarray['CodecID']); 
  41. $track_info['default'] = (isset($trackarray['FlagDefault']) ? $trackarray['FlagDefault'] : true); 
  42. if (isset($trackarray['Name'])) { $track_info['name'] = $trackarray['Name']; } 
  43.  
  44. switch ($trackarray['TrackType']) { 
  45.  
  46. case 1: // Video 
  47. $track_info['resolution_x'] = $trackarray['PixelWidth']; 
  48. $track_info['resolution_y'] = $trackarray['PixelHeight']; 
  49. $track_info['display_unit'] = self::displayUnit(isset($trackarray['DisplayUnit']) ? $trackarray['DisplayUnit'] : 0); 
  50. $track_info['display_x'] = (isset($trackarray['DisplayWidth']) ? $trackarray['DisplayWidth'] : $trackarray['PixelWidth']); 
  51. $track_info['display_y'] = (isset($trackarray['DisplayHeight']) ? $trackarray['DisplayHeight'] : $trackarray['PixelHeight']); 
  52.  
  53. if (isset($trackarray['PixelCropBottom'])) { $track_info['crop_bottom'] = $trackarray['PixelCropBottom']; } 
  54. if (isset($trackarray['PixelCropTop'])) { $track_info['crop_top'] = $trackarray['PixelCropTop']; } 
  55. if (isset($trackarray['PixelCropLeft'])) { $track_info['crop_left'] = $trackarray['PixelCropLeft']; } 
  56. if (isset($trackarray['PixelCropRight'])) { $track_info['crop_right'] = $trackarray['PixelCropRight']; } 
  57. if (isset($trackarray['DefaultDuration'])) { $track_info['frame_rate'] = round(1000000000 / $trackarray['DefaultDuration'], 3); } 
  58. if (isset($trackarray['CodecName'])) { $track_info['codec'] = $trackarray['CodecName']; } 
  59.  
  60. switch ($trackarray['CodecID']) { 
  61. case 'V_MS/VFW/FOURCC': 
  62. getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio-video.riff.php', __FILE__, true); 
  63.  
  64. $parsed = getid3_riff::ParseBITMAPINFOHEADER($trackarray['CodecPrivate']); 
  65. $track_info['codec'] = getid3_riff::fourccLookup($parsed['fourcc']); 
  66. $info['matroska']['track_codec_parsed'][$trackarray['TrackNumber']] = $parsed; 
  67. break; 
  68.  
  69. /**case 'V_MPEG4/ISO/AVC': 
  70. $h264['profile'] = getid3_lib::BigEndian2Int(substr($trackarray['CodecPrivate'], 1, 1)); 
  71. $h264['level'] = getid3_lib::BigEndian2Int(substr($trackarray['CodecPrivate'], 3, 1)); 
  72. $rn = getid3_lib::BigEndian2Int(substr($trackarray['CodecPrivate'], 4, 1)); 
  73. $h264['NALUlength'] = ($rn & 3) + 1; 
  74. $rn = getid3_lib::BigEndian2Int(substr($trackarray['CodecPrivate'], 5, 1)); 
  75. $nsps = ($rn & 31); 
  76. $offset = 6; 
  77. for ($i = 0; $i < $nsps; $i ++) { 
  78. $length = getid3_lib::BigEndian2Int(substr($trackarray['CodecPrivate'], $offset, 2)); 
  79. $h264['SPS'][] = substr($trackarray['CodecPrivate'], $offset + 2, $length); 
  80. $offset += 2 + $length; 
  81. $npps = getid3_lib::BigEndian2Int(substr($trackarray['CodecPrivate'], $offset, 1)); 
  82. $offset += 1; 
  83. for ($i = 0; $i < $npps; $i ++) { 
  84. $length = getid3_lib::BigEndian2Int(substr($trackarray['CodecPrivate'], $offset, 2)); 
  85. $h264['PPS'][] = substr($trackarray['CodecPrivate'], $offset + 2, $length); 
  86. $offset += 2 + $length; 
  87. $info['matroska']['track_codec_parsed'][$trackarray['TrackNumber']] = $h264; 
  88. break;*/ 
  89.  
  90. $info['video']['streams'][] = $track_info; 
  91. break; 
  92.  
  93. case 2: // Audio 
  94. $track_info['sample_rate'] = (isset($trackarray['SamplingFrequency']) ? $trackarray['SamplingFrequency'] : 8000.0); 
  95. $track_info['channels'] = (isset($trackarray['Channels']) ? $trackarray['Channels'] : 1); 
  96. $track_info['language'] = (isset($trackarray['Language']) ? $trackarray['Language'] : 'eng'); 
  97. if (isset($trackarray['BitDepth'])) { $track_info['bits_per_sample'] = $trackarray['BitDepth']; } 
  98. if (isset($trackarray['CodecName'])) { $track_info['codec'] = $trackarray['CodecName']; } 
  99.  
  100. switch ($trackarray['CodecID']) { 
  101. case 'A_PCM/INT/LIT': 
  102. case 'A_PCM/INT/BIG': 
  103. $track_info['bitrate'] = $trackarray['SamplingFrequency'] * $trackarray['Channels'] * $trackarray['BitDepth']; 
  104. break; 
  105.  
  106. case 'A_AC3': 
  107. case 'A_DTS': 
  108. case 'A_MPEG/L3': 
  109. case 'A_MPEG/L2': 
  110. case 'A_FLAC': 
  111. getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.'.($track_info['dataformat'] == 'mp2' ? 'mp3' : $track_info['dataformat']).'.php', __FILE__, true); 
  112.  
  113. if (!isset($info['matroska']['track_data_offsets'][$trackarray['TrackNumber']])) { 
  114. $this->warning('Unable to parse audio data ['.basename(__FILE__).':'.__LINE__.'] because $info[matroska][track_data_offsets]['.$trackarray['TrackNumber'].'] not set'); 
  115. break; 
  116.  
  117. // create temp instance 
  118. $getid3_temp = new getID3(); 
  119. if ($track_info['dataformat'] != 'flac') { 
  120. $getid3_temp->openfile($this->getid3->filename); 
  121. $getid3_temp->info['avdataoffset'] = $info['matroska']['track_data_offsets'][$trackarray['TrackNumber']]['offset']; 
  122. if ($track_info['dataformat'][0] == 'm' || $track_info['dataformat'] == 'flac') { 
  123. $getid3_temp->info['avdataend'] = $info['matroska']['track_data_offsets'][$trackarray['TrackNumber']]['offset'] + $info['matroska']['track_data_offsets'][$trackarray['TrackNumber']]['length']; 
  124.  
  125. // analyze 
  126. $class = 'getid3_'.($track_info['dataformat'] == 'mp2' ? 'mp3' : $track_info['dataformat']); 
  127. $header_data_key = $track_info['dataformat'][0] == 'm' ? 'mpeg' : $track_info['dataformat']; 
  128. $getid3_audio = new $class($getid3_temp, __CLASS__); 
  129. if ($track_info['dataformat'] == 'flac') { 
  130. $getid3_audio->AnalyzeString($trackarray['CodecPrivate']); 
  131. else { 
  132. $getid3_audio->Analyze(); 
  133. if (!empty($getid3_temp->info[$header_data_key])) { 
  134. $info['matroska']['track_codec_parsed'][$trackarray['TrackNumber']] = $getid3_temp->info[$header_data_key]; 
  135. if (isset($getid3_temp->info['audio']) && is_array($getid3_temp->info['audio'])) { 
  136. foreach ($getid3_temp->info['audio'] as $key => $value) { 
  137. $track_info[$key] = $value; 
  138. else { 
  139. $this->warning('Unable to parse audio data ['.basename(__FILE__).':'.__LINE__.'] because '.$class.'::Analyze() failed at offset '.$getid3_temp->info['avdataoffset']); 
  140.  
  141. // copy errors and warnings 
  142. if (!empty($getid3_temp->info['error'])) { 
  143. foreach ($getid3_temp->info['error'] as $newerror) { 
  144. $this->warning($class.'() says: ['.$newerror.']'); 
  145. if (!empty($getid3_temp->info['warning'])) { 
  146. foreach ($getid3_temp->info['warning'] as $newerror) { 
  147. $this->warning($class.'() says: ['.$newerror.']'); 
  148. unset($getid3_temp, $getid3_audio); 
  149. break; 
  150.  
  151. case 'A_AAC': 
  152. case 'A_AAC/MPEG2/LC': 
  153. case 'A_AAC/MPEG2/LC/SBR': 
  154. case 'A_AAC/MPEG4/LC': 
  155. case 'A_AAC/MPEG4/LC/SBR': 
  156. $this->warning($trackarray['CodecID'].' audio data contains no header, audio/video bitrates can\'t be calculated'); 
  157. break; 
  158.  
  159. case 'A_VORBIS': 
  160. if (!isset($trackarray['CodecPrivate'])) { 
  161. $this->warning('Unable to parse audio data ['.basename(__FILE__).':'.__LINE__.'] because CodecPrivate data not set'); 
  162. break; 
  163. $vorbis_offset = strpos($trackarray['CodecPrivate'], 'vorbis', 1); 
  164. if ($vorbis_offset === false) { 
  165. $this->warning('Unable to parse audio data ['.basename(__FILE__).':'.__LINE__.'] because CodecPrivate data does not contain "vorbis" keyword'); 
  166. break; 
  167. $vorbis_offset -= 1; 
  168.  
  169. getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.ogg.php', __FILE__, true); 
  170.  
  171. // create temp instance 
  172. $getid3_temp = new getID3(); 
  173.  
  174. // analyze 
  175. $getid3_ogg = new getid3_ogg($getid3_temp); 
  176. $oggpageinfo['page_seqno'] = 0; 
  177. $getid3_ogg->ParseVorbisPageHeader($trackarray['CodecPrivate'], $vorbis_offset, $oggpageinfo); 
  178. if (!empty($getid3_temp->info['ogg'])) { 
  179. $info['matroska']['track_codec_parsed'][$trackarray['TrackNumber']] = $getid3_temp->info['ogg']; 
  180. if (isset($getid3_temp->info['audio']) && is_array($getid3_temp->info['audio'])) { 
  181. foreach ($getid3_temp->info['audio'] as $key => $value) { 
  182. $track_info[$key] = $value; 
  183.  
  184. // copy errors and warnings 
  185. if (!empty($getid3_temp->info['error'])) { 
  186. foreach ($getid3_temp->info['error'] as $newerror) { 
  187. $this->warning('getid3_ogg() says: ['.$newerror.']'); 
  188. if (!empty($getid3_temp->info['warning'])) { 
  189. foreach ($getid3_temp->info['warning'] as $newerror) { 
  190. $this->warning('getid3_ogg() says: ['.$newerror.']'); 
  191.  
  192. if (!empty($getid3_temp->info['ogg']['bitrate_nominal'])) { 
  193. $track_info['bitrate'] = $getid3_temp->info['ogg']['bitrate_nominal']; 
  194. unset($getid3_temp, $getid3_ogg, $oggpageinfo, $vorbis_offset); 
  195. break; 
  196.  
  197. case 'A_MS/ACM': 
  198. getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio-video.riff.php', __FILE__, true); 
  199.  
  200. $parsed = getid3_riff::parseWAVEFORMATex($trackarray['CodecPrivate']); 
  201. foreach ($parsed as $key => $value) { 
  202. if ($key != 'raw') { 
  203. $track_info[$key] = $value; 
  204. $info['matroska']['track_codec_parsed'][$trackarray['TrackNumber']] = $parsed; 
  205. break; 
  206.  
  207. default: 
  208. $this->warning('Unhandled audio type "'.(isset($trackarray['CodecID']) ? $trackarray['CodecID'] : '').'"'); 
  209.  
  210. $info['audio']['streams'][] = $track_info; 
  211. break; 
  212.  
  213. if (!empty($info['video']['streams'])) { 
  214. $info['video'] = self::getDefaultStreamInfo($info['video']['streams']); 
  215. if (!empty($info['audio']['streams'])) { 
  216. $info['audio'] = self::getDefaultStreamInfo($info['audio']['streams']); 
  217.  
  218. // process attachments 
  219. if (isset($info['matroska']['attachments']) && $this->getid3->option_save_attachments !== getID3::ATTACHMENTS_NONE) { 
  220. foreach ($info['matroska']['attachments'] as $i => $entry) { 
  221. if (strpos($entry['FileMimeType'], 'image/') === 0 && !empty($entry['FileData'])) { 
  222. $info['matroska']['comments']['picture'][] = array('data' => $entry['FileData'], 'image_mime' => $entry['FileMimeType'], 'filename' => $entry['FileName']); 
  223.  
  224. // determine mime type 
  225. if (!empty($info['video']['streams'])) { 
  226. $info['mime_type'] = ($info['matroska']['doctype'] == 'webm' ? 'video/webm' : 'video/x-matroska'); 
  227. } elseif (!empty($info['audio']['streams'])) { 
  228. $info['mime_type'] = ($info['matroska']['doctype'] == 'webm' ? 'audio/webm' : 'audio/x-matroska'); 
  229. } elseif (isset($info['mime_type'])) { 
  230. unset($info['mime_type']); 
  231.  
  232. return true; 
  233.  
  234. private function parseEBML(&$info) { 
  235. // http://www.matroska.org/technical/specs/index.html#EBMLBasics 
  236. $this->current_offset = $info['avdataoffset']; 
  237.  
  238. while ($this->getEBMLelement($top_element, $info['avdataend'])) { 
  239. switch ($top_element['id']) { 
  240.  
  241. case EBML_ID_EBML: 
  242. $info['matroska']['header']['offset'] = $top_element['offset']; 
  243. $info['matroska']['header']['length'] = $top_element['length']; 
  244.  
  245. while ($this->getEBMLelement($element_data, $top_element['end'], true)) { 
  246. switch ($element_data['id']) { 
  247.  
  248. case EBML_ID_EBMLVERSION: 
  249. case EBML_ID_EBMLREADVERSION: 
  250. case EBML_ID_EBMLMAXIDLENGTH: 
  251. case EBML_ID_EBMLMAXSIZELENGTH: 
  252. case EBML_ID_DOCTYPEVERSION: 
  253. case EBML_ID_DOCTYPEREADVERSION: 
  254. $element_data['data'] = getid3_lib::BigEndian2Int($element_data['data']); 
  255. break; 
  256.  
  257. case EBML_ID_DOCTYPE: 
  258. $element_data['data'] = getid3_lib::trimNullByte($element_data['data']); 
  259. $info['matroska']['doctype'] = $element_data['data']; 
  260. $info['fileformat'] = $element_data['data']; 
  261. break; 
  262.  
  263. default: 
  264. $this->unhandledElement('header', __LINE__, $element_data); 
  265.  
  266. unset($element_data['offset'], $element_data['end']); 
  267. $info['matroska']['header']['elements'][] = $element_data; 
  268. break; 
  269.  
  270. case EBML_ID_SEGMENT: 
  271. $info['matroska']['segment'][0]['offset'] = $top_element['offset']; 
  272. $info['matroska']['segment'][0]['length'] = $top_element['length']; 
  273.  
  274. while ($this->getEBMLelement($element_data, $top_element['end'])) { 
  275. if ($element_data['id'] != EBML_ID_CLUSTER || !self::$hide_clusters) { // collect clusters only if required 
  276. $info['matroska']['segments'][] = $element_data; 
  277. switch ($element_data['id']) { 
  278.  
  279. case EBML_ID_SEEKHEAD: // Contains the position of other level 1 elements. 
  280.  
  281. while ($this->getEBMLelement($seek_entry, $element_data['end'])) { 
  282. switch ($seek_entry['id']) { 
  283.  
  284. case EBML_ID_SEEK: // Contains a single seek entry to an EBML element 
  285. while ($this->getEBMLelement($sub_seek_entry, $seek_entry['end'], true)) { 
  286.  
  287. switch ($sub_seek_entry['id']) { 
  288.  
  289. case EBML_ID_SEEKID: 
  290. $seek_entry['target_id'] = self::EBML2Int($sub_seek_entry['data']); 
  291. $seek_entry['target_name'] = self::EBMLidName($seek_entry['target_id']); 
  292. break; 
  293.  
  294. case EBML_ID_SEEKPOSITION: 
  295. $seek_entry['target_offset'] = $element_data['offset'] + getid3_lib::BigEndian2Int($sub_seek_entry['data']); 
  296. break; 
  297.  
  298. default: 
  299. $this->unhandledElement('seekhead.seek', __LINE__, $sub_seek_entry); } 
  300.  
  301. if ($seek_entry['target_id'] != EBML_ID_CLUSTER || !self::$hide_clusters) { // collect clusters only if required 
  302. $info['matroska']['seek'][] = $seek_entry; 
  303. break; 
  304.  
  305. default: 
  306. $this->unhandledElement('seekhead', __LINE__, $seek_entry); 
  307. break; 
  308.  
  309. case EBML_ID_TRACKS: // A top-level block of information with many tracks described. 
  310. $info['matroska']['tracks'] = $element_data; 
  311.  
  312. while ($this->getEBMLelement($track_entry, $element_data['end'])) { 
  313. switch ($track_entry['id']) { 
  314.  
  315. case EBML_ID_TRACKENTRY: //subelements: Describes a track with all elements. 
  316.  
  317. while ($this->getEBMLelement($subelement, $track_entry['end'], array(EBML_ID_VIDEO, EBML_ID_AUDIO, EBML_ID_CONTENTENCODINGS, EBML_ID_CODECPRIVATE))) { 
  318. switch ($subelement['id']) { 
  319.  
  320. case EBML_ID_TRACKNUMBER: 
  321. case EBML_ID_TRACKUID: 
  322. case EBML_ID_TRACKTYPE: 
  323. case EBML_ID_MINCACHE: 
  324. case EBML_ID_MAXCACHE: 
  325. case EBML_ID_MAXBLOCKADDITIONID: 
  326. case EBML_ID_DEFAULTDURATION: // nanoseconds per frame 
  327. $track_entry[$subelement['id_name']] = getid3_lib::BigEndian2Int($subelement['data']); 
  328. break; 
  329.  
  330. case EBML_ID_TRACKTIMECODESCALE: 
  331. $track_entry[$subelement['id_name']] = getid3_lib::BigEndian2Float($subelement['data']); 
  332. break; 
  333.  
  334. case EBML_ID_CODECID: 
  335. case EBML_ID_LANGUAGE: 
  336. case EBML_ID_NAME: 
  337. case EBML_ID_CODECNAME: 
  338. $track_entry[$subelement['id_name']] = getid3_lib::trimNullByte($subelement['data']); 
  339. break; 
  340.  
  341. case EBML_ID_CODECPRIVATE: 
  342. $track_entry[$subelement['id_name']] = $this->readEBMLelementData($subelement['length'], true); 
  343. break; 
  344.  
  345. case EBML_ID_FLAGENABLED: 
  346. case EBML_ID_FLAGDEFAULT: 
  347. case EBML_ID_FLAGFORCED: 
  348. case EBML_ID_FLAGLACING: 
  349. case EBML_ID_CODECDECODEALL: 
  350. $track_entry[$subelement['id_name']] = (bool) getid3_lib::BigEndian2Int($subelement['data']); 
  351. break; 
  352.  
  353. case EBML_ID_VIDEO: 
  354.  
  355. while ($this->getEBMLelement($sub_subelement, $subelement['end'], true)) { 
  356. switch ($sub_subelement['id']) { 
  357.  
  358. case EBML_ID_PIXELWIDTH: 
  359. case EBML_ID_PIXELHEIGHT: 
  360. case EBML_ID_PIXELCROPBOTTOM: 
  361. case EBML_ID_PIXELCROPTOP: 
  362. case EBML_ID_PIXELCROPLEFT: 
  363. case EBML_ID_PIXELCROPRIGHT: 
  364. case EBML_ID_DISPLAYWIDTH: 
  365. case EBML_ID_DISPLAYHEIGHT: 
  366. case EBML_ID_DISPLAYUNIT: 
  367. case EBML_ID_ASPECTRATIOTYPE: 
  368. case EBML_ID_STEREOMODE: 
  369. case EBML_ID_OLDSTEREOMODE: 
  370. $track_entry[$sub_subelement['id_name']] = getid3_lib::BigEndian2Int($sub_subelement['data']); 
  371. break; 
  372.  
  373. case EBML_ID_FLAGINTERLACED: 
  374. $track_entry[$sub_subelement['id_name']] = (bool)getid3_lib::BigEndian2Int($sub_subelement['data']); 
  375. break; 
  376.  
  377. case EBML_ID_GAMMAVALUE: 
  378. $track_entry[$sub_subelement['id_name']] = getid3_lib::BigEndian2Float($sub_subelement['data']); 
  379. break; 
  380.  
  381. case EBML_ID_COLOURSPACE: 
  382. $track_entry[$sub_subelement['id_name']] = getid3_lib::trimNullByte($sub_subelement['data']); 
  383. break; 
  384.  
  385. default: 
  386. $this->unhandledElement('track.video', __LINE__, $sub_subelement); 
  387. break; 
  388.  
  389. case EBML_ID_AUDIO: 
  390.  
  391. while ($this->getEBMLelement($sub_subelement, $subelement['end'], true)) { 
  392. switch ($sub_subelement['id']) { 
  393.  
  394. case EBML_ID_CHANNELS: 
  395. case EBML_ID_BITDEPTH: 
  396. $track_entry[$sub_subelement['id_name']] = getid3_lib::BigEndian2Int($sub_subelement['data']); 
  397. break; 
  398.  
  399. case EBML_ID_SAMPLINGFREQUENCY: 
  400. case EBML_ID_OUTPUTSAMPLINGFREQUENCY: 
  401. $track_entry[$sub_subelement['id_name']] = getid3_lib::BigEndian2Float($sub_subelement['data']); 
  402. break; 
  403.  
  404. case EBML_ID_CHANNELPOSITIONS: 
  405. $track_entry[$sub_subelement['id_name']] = getid3_lib::trimNullByte($sub_subelement['data']); 
  406. break; 
  407.  
  408. default: 
  409. $this->unhandledElement('track.audio', __LINE__, $sub_subelement); 
  410. break; 
  411.  
  412. case EBML_ID_CONTENTENCODINGS: 
  413.  
  414. while ($this->getEBMLelement($sub_subelement, $subelement['end'])) { 
  415. switch ($sub_subelement['id']) { 
  416.  
  417. case EBML_ID_CONTENTENCODING: 
  418.  
  419. while ($this->getEBMLelement($sub_sub_subelement, $sub_subelement['end'], array(EBML_ID_CONTENTCOMPRESSION, EBML_ID_CONTENTENCRYPTION))) { 
  420. switch ($sub_sub_subelement['id']) { 
  421.  
  422. case EBML_ID_CONTENTENCODINGORDER: 
  423. case EBML_ID_CONTENTENCODINGSCOPE: 
  424. case EBML_ID_CONTENTENCODINGTYPE: 
  425. $track_entry[$sub_subelement['id_name']][$sub_sub_subelement['id_name']] = getid3_lib::BigEndian2Int($sub_sub_subelement['data']); 
  426. break; 
  427.  
  428. case EBML_ID_CONTENTCOMPRESSION: 
  429.  
  430. while ($this->getEBMLelement($sub_sub_sub_subelement, $sub_sub_subelement['end'], true)) { 
  431. switch ($sub_sub_sub_subelement['id']) { 
  432.  
  433. case EBML_ID_CONTENTCOMPALGO: 
  434. $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']); 
  435. break; 
  436.  
  437. case EBML_ID_CONTENTCOMPSETTINGS: 
  438. $track_entry[$sub_subelement['id_name']][$sub_sub_subelement['id_name']][$sub_sub_sub_subelement['id_name']] = $sub_sub_sub_subelement['data']; 
  439. break; 
  440.  
  441. default: 
  442. $this->unhandledElement('track.contentencodings.contentencoding.contentcompression', __LINE__, $sub_sub_sub_subelement); 
  443. break; 
  444.  
  445. case EBML_ID_CONTENTENCRYPTION: 
  446.  
  447. while ($this->getEBMLelement($sub_sub_sub_subelement, $sub_sub_subelement['end'], true)) { 
  448. switch ($sub_sub_sub_subelement['id']) { 
  449.  
  450. case EBML_ID_CONTENTENCALGO: 
  451. case EBML_ID_CONTENTSIGALGO: 
  452. case EBML_ID_CONTENTSIGHASHALGO: 
  453. $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']); 
  454. break; 
  455.  
  456. case EBML_ID_CONTENTENCKEYID: 
  457. case EBML_ID_CONTENTSIGNATURE: 
  458. case EBML_ID_CONTENTSIGKEYID: 
  459. $track_entry[$sub_subelement['id_name']][$sub_sub_subelement['id_name']][$sub_sub_sub_subelement['id_name']] = $sub_sub_sub_subelement['data']; 
  460. break; 
  461.  
  462. default: 
  463. $this->unhandledElement('track.contentencodings.contentencoding.contentcompression', __LINE__, $sub_sub_sub_subelement); 
  464. break; 
  465.  
  466. default: 
  467. $this->unhandledElement('track.contentencodings.contentencoding', __LINE__, $sub_sub_subelement); 
  468. break; 
  469.  
  470. default: 
  471. $this->unhandledElement('track.contentencodings', __LINE__, $sub_subelement); 
  472. break; 
  473.  
  474. default: 
  475. $this->unhandledElement('track', __LINE__, $subelement); 
  476.  
  477. $info['matroska']['tracks']['tracks'][] = $track_entry; 
  478. break; 
  479.  
  480. default: 
  481. $this->unhandledElement('tracks', __LINE__, $track_entry); 
  482. break; 
  483.  
  484. case EBML_ID_INFO: // Contains miscellaneous general information and statistics on the file. 
  485. $info_entry = array(); 
  486.  
  487. while ($this->getEBMLelement($subelement, $element_data['end'], true)) { 
  488. switch ($subelement['id']) { 
  489.  
  490. case EBML_ID_TIMECODESCALE: 
  491. $info_entry[$subelement['id_name']] = getid3_lib::BigEndian2Int($subelement['data']); 
  492. break; 
  493.  
  494. case EBML_ID_DURATION: 
  495. $info_entry[$subelement['id_name']] = getid3_lib::BigEndian2Float($subelement['data']); 
  496. break; 
  497.  
  498. case EBML_ID_DATEUTC: 
  499. $info_entry[$subelement['id_name']] = getid3_lib::BigEndian2Int($subelement['data']); 
  500. $info_entry[$subelement['id_name'].'_unix'] = self::EBMLdate2unix($info_entry[$subelement['id_name']]); 
  501. break; 
  502.  
  503. case EBML_ID_SEGMENTUID: 
  504. case EBML_ID_PREVUID: 
  505. case EBML_ID_NEXTUID: 
  506. $info_entry[$subelement['id_name']] = getid3_lib::trimNullByte($subelement['data']); 
  507. break; 
  508.  
  509. case EBML_ID_SEGMENTFAMILY: 
  510. $info_entry[$subelement['id_name']][] = getid3_lib::trimNullByte($subelement['data']); 
  511. break; 
  512.  
  513. case EBML_ID_SEGMENTFILENAME: 
  514. case EBML_ID_PREVFILENAME: 
  515. case EBML_ID_NEXTFILENAME: 
  516. case EBML_ID_TITLE: 
  517. case EBML_ID_MUXINGAPP: 
  518. case EBML_ID_WRITINGAPP: 
  519. $info_entry[$subelement['id_name']] = getid3_lib::trimNullByte($subelement['data']); 
  520. $info['matroska']['comments'][strtolower($subelement['id_name'])][] = $info_entry[$subelement['id_name']]; 
  521. break; 
  522.  
  523. case EBML_ID_CHAPTERTRANSLATE: 
  524. $chaptertranslate_entry = array(); 
  525.  
  526. while ($this->getEBMLelement($sub_subelement, $subelement['end'], true)) { 
  527. switch ($sub_subelement['id']) { 
  528.  
  529. case EBML_ID_CHAPTERTRANSLATEEDITIONUID: 
  530. $chaptertranslate_entry[$sub_subelement['id_name']][] = getid3_lib::BigEndian2Int($sub_subelement['data']); 
  531. break; 
  532.  
  533. case EBML_ID_CHAPTERTRANSLATECODEC: 
  534. $chaptertranslate_entry[$sub_subelement['id_name']] = getid3_lib::BigEndian2Int($sub_subelement['data']); 
  535. break; 
  536.  
  537. case EBML_ID_CHAPTERTRANSLATEID: 
  538. $chaptertranslate_entry[$sub_subelement['id_name']] = getid3_lib::trimNullByte($sub_subelement['data']); 
  539. break; 
  540.  
  541. default: 
  542. $this->unhandledElement('info.chaptertranslate', __LINE__, $sub_subelement); 
  543. $info_entry[$subelement['id_name']] = $chaptertranslate_entry; 
  544. break; 
  545.  
  546. default: 
  547. $this->unhandledElement('info', __LINE__, $subelement); 
  548. $info['matroska']['info'][] = $info_entry; 
  549. break; 
  550.  
  551. 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. 
  552. if (self::$hide_clusters) { // do not parse cues if hide clusters is "ON" till they point to clusters anyway 
  553. $this->current_offset = $element_data['end']; 
  554. break; 
  555. $cues_entry = array(); 
  556.  
  557. while ($this->getEBMLelement($subelement, $element_data['end'])) { 
  558. switch ($subelement['id']) { 
  559.  
  560. case EBML_ID_CUEPOINT: 
  561. $cuepoint_entry = array(); 
  562.  
  563. while ($this->getEBMLelement($sub_subelement, $subelement['end'], array(EBML_ID_CUETRACKPOSITIONS))) { 
  564. switch ($sub_subelement['id']) { 
  565.  
  566. case EBML_ID_CUETRACKPOSITIONS: 
  567. $cuetrackpositions_entry = array(); 
  568.  
  569. while ($this->getEBMLelement($sub_sub_subelement, $sub_subelement['end'], true)) { 
  570. switch ($sub_sub_subelement['id']) { 
  571.  
  572. case EBML_ID_CUETRACK: 
  573. case EBML_ID_CUECLUSTERPOSITION: 
  574. case EBML_ID_CUEBLOCKNUMBER: 
  575. case EBML_ID_CUECODECSTATE: 
  576. $cuetrackpositions_entry[$sub_sub_subelement['id_name']] = getid3_lib::BigEndian2Int($sub_sub_subelement['data']); 
  577. break; 
  578.  
  579. default: 
  580. $this->unhandledElement('cues.cuepoint.cuetrackpositions', __LINE__, $sub_sub_subelement); 
  581. $cuepoint_entry[$sub_subelement['id_name']][] = $cuetrackpositions_entry; 
  582. break; 
  583.  
  584. case EBML_ID_CUETIME: 
  585. $cuepoint_entry[$sub_subelement['id_name']] = getid3_lib::BigEndian2Int($sub_subelement['data']); 
  586. break; 
  587.  
  588. default: 
  589. $this->unhandledElement('cues.cuepoint', __LINE__, $sub_subelement); 
  590. $cues_entry[] = $cuepoint_entry; 
  591. break; 
  592.  
  593. default: 
  594. $this->unhandledElement('cues', __LINE__, $subelement); 
  595. $info['matroska']['cues'] = $cues_entry; 
  596. break; 
  597.  
  598. case EBML_ID_TAGS: // Element containing elements specific to Tracks/Chapters. 
  599. $tags_entry = array(); 
  600.  
  601. while ($this->getEBMLelement($subelement, $element_data['end'], false)) { 
  602. switch ($subelement['id']) { 
  603.  
  604. case EBML_ID_TAG: 
  605. $tag_entry = array(); 
  606.  
  607. while ($this->getEBMLelement($sub_subelement, $subelement['end'], false)) { 
  608. switch ($sub_subelement['id']) { 
  609.  
  610. case EBML_ID_TARGETS: 
  611. $targets_entry = array(); 
  612.  
  613. while ($this->getEBMLelement($sub_sub_subelement, $sub_subelement['end'], true)) { 
  614. switch ($sub_sub_subelement['id']) { 
  615.  
  616. case EBML_ID_TARGETTYPEVALUE: 
  617. $targets_entry[$sub_sub_subelement['id_name']] = getid3_lib::BigEndian2Int($sub_sub_subelement['data']); 
  618. $targets_entry[strtolower($sub_sub_subelement['id_name']).'_long'] = self::TargetTypeValue($targets_entry[$sub_sub_subelement['id_name']]); 
  619. break; 
  620.  
  621. case EBML_ID_TARGETTYPE: 
  622. $targets_entry[$sub_sub_subelement['id_name']] = $sub_sub_subelement['data']; 
  623. break; 
  624.  
  625. case EBML_ID_TAGTRACKUID: 
  626. case EBML_ID_TAGEDITIONUID: 
  627. case EBML_ID_TAGCHAPTERUID: 
  628. case EBML_ID_TAGATTACHMENTUID: 
  629. $targets_entry[$sub_sub_subelement['id_name']][] = getid3_lib::BigEndian2Int($sub_sub_subelement['data']); 
  630. break; 
  631.  
  632. default: 
  633. $this->unhandledElement('tags.tag.targets', __LINE__, $sub_sub_subelement); 
  634. $tag_entry[$sub_subelement['id_name']] = $targets_entry; 
  635. break; 
  636.  
  637. case EBML_ID_SIMPLETAG: 
  638. $tag_entry[$sub_subelement['id_name']][] = $this->HandleEMBLSimpleTag($sub_subelement['end']); 
  639. break; 
  640.  
  641. default: 
  642. $this->unhandledElement('tags.tag', __LINE__, $sub_subelement); 
  643. $tags_entry[] = $tag_entry; 
  644. break; 
  645.  
  646. default: 
  647. $this->unhandledElement('tags', __LINE__, $subelement); 
  648. $info['matroska']['tags'] = $tags_entry; 
  649. break; 
  650.  
  651. case EBML_ID_ATTACHMENTS: // Contain attached files. 
  652.  
  653. while ($this->getEBMLelement($subelement, $element_data['end'])) { 
  654. switch ($subelement['id']) { 
  655.  
  656. case EBML_ID_ATTACHEDFILE: 
  657. $attachedfile_entry = array(); 
  658.  
  659. while ($this->getEBMLelement($sub_subelement, $subelement['end'], array(EBML_ID_FILEDATA))) { 
  660. switch ($sub_subelement['id']) { 
  661.  
  662. case EBML_ID_FILEDESCRIPTION: 
  663. case EBML_ID_FILENAME: 
  664. case EBML_ID_FILEMIMETYPE: 
  665. $attachedfile_entry[$sub_subelement['id_name']] = $sub_subelement['data']; 
  666. break; 
  667.  
  668. case EBML_ID_FILEDATA: 
  669. $attachedfile_entry['data_offset'] = $this->current_offset; 
  670. $attachedfile_entry['data_length'] = $sub_subelement['length']; 
  671.  
  672. $attachedfile_entry[$sub_subelement['id_name']] = $this->saveAttachment( 
  673. $attachedfile_entry['FileName'],  
  674. $attachedfile_entry['data_offset'],  
  675. $attachedfile_entry['data_length']); 
  676.  
  677. $this->current_offset = $sub_subelement['end']; 
  678. break; 
  679.  
  680. case EBML_ID_FILEUID: 
  681. $attachedfile_entry[$sub_subelement['id_name']] = getid3_lib::BigEndian2Int($sub_subelement['data']); 
  682. break; 
  683.  
  684. default: 
  685. $this->unhandledElement('attachments.attachedfile', __LINE__, $sub_subelement); 
  686. $info['matroska']['attachments'][] = $attachedfile_entry; 
  687. break; 
  688.  
  689. default: 
  690. $this->unhandledElement('attachments', __LINE__, $subelement); 
  691. break; 
  692.  
  693. case EBML_ID_CHAPTERS: 
  694.  
  695. while ($this->getEBMLelement($subelement, $element_data['end'])) { 
  696. switch ($subelement['id']) { 
  697.  
  698. case EBML_ID_EDITIONENTRY: 
  699. $editionentry_entry = array(); 
  700.  
  701. while ($this->getEBMLelement($sub_subelement, $subelement['end'], array(EBML_ID_CHAPTERATOM))) { 
  702. switch ($sub_subelement['id']) { 
  703.  
  704. case EBML_ID_EDITIONUID: 
  705. $editionentry_entry[$sub_subelement['id_name']] = getid3_lib::BigEndian2Int($sub_subelement['data']); 
  706. break; 
  707.  
  708. case EBML_ID_EDITIONFLAGHIDDEN: 
  709. case EBML_ID_EDITIONFLAGDEFAULT: 
  710. case EBML_ID_EDITIONFLAGORDERED: 
  711. $editionentry_entry[$sub_subelement['id_name']] = (bool)getid3_lib::BigEndian2Int($sub_subelement['data']); 
  712. break; 
  713.  
  714. case EBML_ID_CHAPTERATOM: 
  715. $chapteratom_entry = array(); 
  716.  
  717. while ($this->getEBMLelement($sub_sub_subelement, $sub_subelement['end'], array(EBML_ID_CHAPTERTRACK, EBML_ID_CHAPTERDISPLAY))) { 
  718. switch ($sub_sub_subelement['id']) { 
  719.  
  720. case EBML_ID_CHAPTERSEGMENTUID: 
  721. case EBML_ID_CHAPTERSEGMENTEDITIONUID: 
  722. $chapteratom_entry[$sub_sub_subelement['id_name']] = $sub_sub_subelement['data']; 
  723. break; 
  724.  
  725. case EBML_ID_CHAPTERFLAGENABLED: 
  726. case EBML_ID_CHAPTERFLAGHIDDEN: 
  727. $chapteratom_entry[$sub_sub_subelement['id_name']] = (bool)getid3_lib::BigEndian2Int($sub_sub_subelement['data']); 
  728. break; 
  729.  
  730. case EBML_ID_CHAPTERUID: 
  731. case EBML_ID_CHAPTERTIMESTART: 
  732. case EBML_ID_CHAPTERTIMEEND: 
  733. $chapteratom_entry[$sub_sub_subelement['id_name']] = getid3_lib::BigEndian2Int($sub_sub_subelement['data']); 
  734. break; 
  735.  
  736. case EBML_ID_CHAPTERTRACK: 
  737. $chaptertrack_entry = array(); 
  738.  
  739. while ($this->getEBMLelement($sub_sub_sub_subelement, $sub_sub_subelement['end'], true)) { 
  740. switch ($sub_sub_sub_subelement['id']) { 
  741.  
  742. case EBML_ID_CHAPTERTRACKNUMBER: 
  743. $chaptertrack_entry[$sub_sub_sub_subelement['id_name']] = getid3_lib::BigEndian2Int($sub_sub_sub_subelement['data']); 
  744. break; 
  745.  
  746. default: 
  747. $this->unhandledElement('chapters.editionentry.chapteratom.chaptertrack', __LINE__, $sub_sub_sub_subelement); 
  748. $chapteratom_entry[$sub_sub_subelement['id_name']][] = $chaptertrack_entry; 
  749. break; 
  750.  
  751. case EBML_ID_CHAPTERDISPLAY: 
  752. $chapterdisplay_entry = array(); 
  753.  
  754. while ($this->getEBMLelement($sub_sub_sub_subelement, $sub_sub_subelement['end'], true)) { 
  755. switch ($sub_sub_sub_subelement['id']) { 
  756.  
  757. case EBML_ID_CHAPSTRING: 
  758. case EBML_ID_CHAPLANGUAGE: 
  759. case EBML_ID_CHAPCOUNTRY: 
  760. $chapterdisplay_entry[$sub_sub_sub_subelement['id_name']] = $sub_sub_sub_subelement['data']; 
  761. break; 
  762.  
  763. default: 
  764. $this->unhandledElement('chapters.editionentry.chapteratom.chapterdisplay', __LINE__, $sub_sub_sub_subelement); 
  765. $chapteratom_entry[$sub_sub_subelement['id_name']][] = $chapterdisplay_entry; 
  766. break; 
  767.  
  768. default: 
  769. $this->unhandledElement('chapters.editionentry.chapteratom', __LINE__, $sub_sub_subelement); 
  770. $editionentry_entry[$sub_subelement['id_name']][] = $chapteratom_entry; 
  771. break; 
  772.  
  773. default: 
  774. $this->unhandledElement('chapters.editionentry', __LINE__, $sub_subelement); 
  775. $info['matroska']['chapters'][] = $editionentry_entry; 
  776. break; 
  777.  
  778. default: 
  779. $this->unhandledElement('chapters', __LINE__, $subelement); 
  780. break; 
  781.  
  782. case EBML_ID_CLUSTER: // The lower level element containing the (monolithic) Block structure. 
  783. $cluster_entry = array(); 
  784.  
  785. while ($this->getEBMLelement($subelement, $element_data['end'], array(EBML_ID_CLUSTERSILENTTRACKS, EBML_ID_CLUSTERBLOCKGROUP, EBML_ID_CLUSTERSIMPLEBLOCK))) { 
  786. switch ($subelement['id']) { 
  787.  
  788. case EBML_ID_CLUSTERTIMECODE: 
  789. case EBML_ID_CLUSTERPOSITION: 
  790. case EBML_ID_CLUSTERPREVSIZE: 
  791. $cluster_entry[$subelement['id_name']] = getid3_lib::BigEndian2Int($subelement['data']); 
  792. break; 
  793.  
  794. case EBML_ID_CLUSTERSILENTTRACKS: 
  795. $cluster_silent_tracks = array(); 
  796.  
  797. while ($this->getEBMLelement($sub_subelement, $subelement['end'], true)) { 
  798. switch ($sub_subelement['id']) { 
  799.  
  800. case EBML_ID_CLUSTERSILENTTRACKNUMBER: 
  801. $cluster_silent_tracks[] = getid3_lib::BigEndian2Int($sub_subelement['data']); 
  802. break; 
  803.  
  804. default: 
  805. $this->unhandledElement('cluster.silenttracks', __LINE__, $sub_subelement); 
  806. $cluster_entry[$subelement['id_name']][] = $cluster_silent_tracks; 
  807. break; 
  808.  
  809. case EBML_ID_CLUSTERBLOCKGROUP: 
  810. $cluster_block_group = array('offset' => $this->current_offset); 
  811.  
  812. while ($this->getEBMLelement($sub_subelement, $subelement['end'], array(EBML_ID_CLUSTERBLOCK))) { 
  813. switch ($sub_subelement['id']) { 
  814.  
  815. case EBML_ID_CLUSTERBLOCK: 
  816. $cluster_block_group[$sub_subelement['id_name']] = $this->HandleEMBLClusterBlock($sub_subelement, EBML_ID_CLUSTERBLOCK, $info); 
  817. break; 
  818.  
  819. case EBML_ID_CLUSTERREFERENCEPRIORITY: // unsigned-int 
  820. case EBML_ID_CLUSTERBLOCKDURATION: // unsigned-int 
  821. $cluster_block_group[$sub_subelement['id_name']] = getid3_lib::BigEndian2Int($sub_subelement['data']); 
  822. break; 
  823.  
  824. case EBML_ID_CLUSTERREFERENCEBLOCK: // signed-int 
  825. $cluster_block_group[$sub_subelement['id_name']][] = getid3_lib::BigEndian2Int($sub_subelement['data'], false, true); 
  826. break; 
  827.  
  828. case EBML_ID_CLUSTERCODECSTATE: 
  829. $cluster_block_group[$sub_subelement['id_name']] = getid3_lib::trimNullByte($sub_subelement['data']); 
  830. break; 
  831.  
  832. default: 
  833. $this->unhandledElement('clusters.blockgroup', __LINE__, $sub_subelement); 
  834. $cluster_entry[$subelement['id_name']][] = $cluster_block_group; 
  835. break; 
  836.  
  837. case EBML_ID_CLUSTERSIMPLEBLOCK: 
  838. $cluster_entry[$subelement['id_name']][] = $this->HandleEMBLClusterBlock($subelement, EBML_ID_CLUSTERSIMPLEBLOCK, $info); 
  839. break; 
  840.  
  841. default: 
  842. $this->unhandledElement('cluster', __LINE__, $subelement); 
  843. $this->current_offset = $subelement['end']; 
  844. if (!self::$hide_clusters) { 
  845. $info['matroska']['cluster'][] = $cluster_entry; 
  846.  
  847. // check to see if all the data we need exists already, if so, break out of the loop 
  848. if (!self::$parse_whole_file) { 
  849. if (isset($info['matroska']['info']) && is_array($info['matroska']['info'])) { 
  850. if (isset($info['matroska']['tracks']['tracks']) && is_array($info['matroska']['tracks']['tracks'])) { 
  851. if (count($info['matroska']['track_data_offsets']) == count($info['matroska']['tracks']['tracks'])) { 
  852. return; 
  853. break; 
  854.  
  855. default: 
  856. $this->unhandledElement('segment', __LINE__, $element_data); 
  857. break; 
  858.  
  859. default: 
  860. $this->unhandledElement('root', __LINE__, $top_element); 
  861.  
  862. private function EnsureBufferHasEnoughData($min_data=1024) { 
  863. if (($this->current_offset - $this->EBMLbuffer_offset) >= ($this->EBMLbuffer_length - $min_data)) { 
  864. $read_bytes = max($min_data, $this->getid3->fread_buffer_size()); 
  865.  
  866. try { 
  867. $this->fseek($this->current_offset); 
  868. $this->EBMLbuffer_offset = $this->current_offset; 
  869. $this->EBMLbuffer = $this->fread($read_bytes); 
  870. $this->EBMLbuffer_length = strlen($this->EBMLbuffer); 
  871. } catch (getid3_exception $e) { 
  872. $this->warning('EBML parser: '.$e->getMessage()); 
  873. return false; 
  874.  
  875. if ($this->EBMLbuffer_length == 0 && $this->feof()) { 
  876. return $this->error('EBML parser: ran out of file at offset '.$this->current_offset); 
  877. return true; 
  878.  
  879. private function readEBMLint() { 
  880. $actual_offset = $this->current_offset - $this->EBMLbuffer_offset; 
  881.  
  882. // get length of integer 
  883. $first_byte_int = ord($this->EBMLbuffer[$actual_offset]); 
  884. if (0x80 & $first_byte_int) { 
  885. $length = 1; 
  886. } elseif (0x40 & $first_byte_int) { 
  887. $length = 2; 
  888. } elseif (0x20 & $first_byte_int) { 
  889. $length = 3; 
  890. } elseif (0x10 & $first_byte_int) { 
  891. $length = 4; 
  892. } elseif (0x08 & $first_byte_int) { 
  893. $length = 5; 
  894. } elseif (0x04 & $first_byte_int) { 
  895. $length = 6; 
  896. } elseif (0x02 & $first_byte_int) { 
  897. $length = 7; 
  898. } elseif (0x01 & $first_byte_int) { 
  899. $length = 8; 
  900. } else { 
  901. throw new Exception('invalid EBML integer (leading 0x00) at '.$this->current_offset); 
  902.  
  903. // read 
  904. $int_value = self::EBML2Int(substr($this->EBMLbuffer, $actual_offset, $length)); 
  905. $this->current_offset += $length; 
  906.  
  907. return $int_value; 
  908.  
  909. private function readEBMLelementData($length, $check_buffer=false) { 
  910. if ($check_buffer && !$this->EnsureBufferHasEnoughData($length)) { 
  911. return false; 
  912. $data = substr($this->EBMLbuffer, $this->current_offset - $this->EBMLbuffer_offset, $length); 
  913. $this->current_offset += $length; 
  914. return $data; 
  915.  
  916. private function getEBMLelement(&$element, $parent_end, $get_data=false) { 
  917. if ($this->current_offset >= $parent_end) { 
  918. return false; 
  919.  
  920. if (!$this->EnsureBufferHasEnoughData()) { 
  921. $this->current_offset = PHP_INT_MAX; // do not exit parser right now, allow to finish current loop to gather maximum information 
  922. return false; 
  923.  
  924. $element = array(); 
  925.  
  926. // set offset 
  927. $element['offset'] = $this->current_offset; 
  928.  
  929. // get ID 
  930. $element['id'] = $this->readEBMLint(); 
  931.  
  932. // get name 
  933. $element['id_name'] = self::EBMLidName($element['id']); 
  934.  
  935. // get length 
  936. $element['length'] = $this->readEBMLint(); 
  937.  
  938. // get end offset 
  939. $element['end'] = $this->current_offset + $element['length']; 
  940.  
  941. // get raw data 
  942. $dont_parse = (in_array($element['id'], $this->unuseful_elements) || $element['id_name'] == dechex($element['id'])); 
  943. if (($get_data === true || (is_array($get_data) && !in_array($element['id'], $get_data))) && !$dont_parse) { 
  944. $element['data'] = $this->readEBMLelementData($element['length'], $element); 
  945.  
  946. return true; 
  947.  
  948. private function unhandledElement($type, $line, $element) { 
  949. // warn only about unknown and missed elements, not about unuseful 
  950. if (!in_array($element['id'], $this->unuseful_elements)) { 
  951. $this->warning('Unhandled '.$type.' element ['.basename(__FILE__).':'.$line.'] ('.$element['id'].'::'.$element['id_name'].' ['.$element['length'].' bytes]) at '.$element['offset']); 
  952.  
  953. // increase offset for unparsed elements 
  954. if (!isset($element['data'])) { 
  955. $this->current_offset = $element['end']; 
  956.  
  957. private function ExtractCommentsSimpleTag($SimpleTagArray) { 
  958. if (!empty($SimpleTagArray['SimpleTag'])) { 
  959. foreach ($SimpleTagArray['SimpleTag'] as $SimpleTagKey => $SimpleTagData) { 
  960. if (!empty($SimpleTagData['TagName']) && !empty($SimpleTagData['TagString'])) { 
  961. $this->getid3->info['matroska']['comments'][strtolower($SimpleTagData['TagName'])][] = $SimpleTagData['TagString']; 
  962. if (!empty($SimpleTagData['SimpleTag'])) { 
  963. $this->ExtractCommentsSimpleTag($SimpleTagData); 
  964.  
  965. return true; 
  966.  
  967. private function HandleEMBLSimpleTag($parent_end) { 
  968. $simpletag_entry = array(); 
  969.  
  970. while ($this->getEBMLelement($element, $parent_end, array(EBML_ID_SIMPLETAG))) { 
  971. switch ($element['id']) { 
  972.  
  973. case EBML_ID_TAGNAME: 
  974. case EBML_ID_TAGLANGUAGE: 
  975. case EBML_ID_TAGSTRING: 
  976. case EBML_ID_TAGBINARY: 
  977. $simpletag_entry[$element['id_name']] = $element['data']; 
  978. break; 
  979.  
  980. case EBML_ID_SIMPLETAG: 
  981. $simpletag_entry[$element['id_name']][] = $this->HandleEMBLSimpleTag($element['end']); 
  982. break; 
  983.  
  984. case EBML_ID_TAGDEFAULT: 
  985. $simpletag_entry[$element['id_name']] = (bool)getid3_lib::BigEndian2Int($element['data']); 
  986. break; 
  987.  
  988. default: 
  989. $this->unhandledElement('tag.simpletag', __LINE__, $element); 
  990.  
  991. return $simpletag_entry; 
  992.  
  993. private function HandleEMBLClusterBlock($element, $block_type, &$info) { 
  994. // http://www.matroska.org/technical/specs/index.html#block_structure 
  995. // http://www.matroska.org/technical/specs/index.html#simpleblock_structure 
  996.  
  997. $block_data = array(); 
  998. $block_data['tracknumber'] = $this->readEBMLint(); 
  999. $block_data['timecode'] = getid3_lib::BigEndian2Int($this->readEBMLelementData(2), false, true); 
  1000. $block_data['flags_raw'] = getid3_lib::BigEndian2Int($this->readEBMLelementData(1)); 
  1001.  
  1002. if ($block_type == EBML_ID_CLUSTERSIMPLEBLOCK) { 
  1003. $block_data['flags']['keyframe'] = (($block_data['flags_raw'] & 0x80) >> 7); 
  1004. //$block_data['flags']['reserved1'] = (($block_data['flags_raw'] & 0x70) >> 4); 
  1005. else { 
  1006. //$block_data['flags']['reserved1'] = (($block_data['flags_raw'] & 0xF0) >> 4); 
  1007. $block_data['flags']['invisible'] = (bool)(($block_data['flags_raw'] & 0x08) >> 3); 
  1008. $block_data['flags']['lacing'] = (($block_data['flags_raw'] & 0x06) >> 1); // 00=no lacing; 01=Xiph lacing; 11=EBML lacing; 10=fixed-size lacing 
  1009. if ($block_type == EBML_ID_CLUSTERSIMPLEBLOCK) { 
  1010. $block_data['flags']['discardable'] = (($block_data['flags_raw'] & 0x01)); 
  1011. else { 
  1012. //$block_data['flags']['reserved2'] = (($block_data['flags_raw'] & 0x01) >> 0); 
  1013. $block_data['flags']['lacing_type'] = self::BlockLacingType($block_data['flags']['lacing']); 
  1014.  
  1015. // Lace (when lacing bit is set) 
  1016. if ($block_data['flags']['lacing'] > 0) { 
  1017. $block_data['lace_frames'] = getid3_lib::BigEndian2Int($this->readEBMLelementData(1)) + 1; // Number of frames in the lace-1 (uint8) 
  1018. if ($block_data['flags']['lacing'] != 0x02) { 
  1019. for ($i = 1; $i < $block_data['lace_frames']; $i ++) { // Lace-coded size of each frame of the lace, except for the last one (multiple uint8). *This is not used with Fixed-size lacing as it is calculated automatically from (total size of lace) / (number of frames in lace). 
  1020. if ($block_data['flags']['lacing'] == 0x03) { // EBML lacing 
  1021. $block_data['lace_frames_size'][$i] = $this->readEBMLint(); // TODO: read size correctly, calc size for the last frame. For now offsets are deteminded OK with readEBMLint() and that's the most important thing. 
  1022. else { // Xiph lacing 
  1023. $block_data['lace_frames_size'][$i] = 0; 
  1024. do { 
  1025. $size = getid3_lib::BigEndian2Int($this->readEBMLelementData(1)); 
  1026. $block_data['lace_frames_size'][$i] += $size; 
  1027. while ($size == 255); 
  1028. if ($block_data['flags']['lacing'] == 0x01) { // calc size of the last frame only for Xiph lacing, till EBML sizes are now anyway determined incorrectly 
  1029. $block_data['lace_frames_size'][] = $element['end'] - $this->current_offset - array_sum($block_data['lace_frames_size']); 
  1030.  
  1031. if (!isset($info['matroska']['track_data_offsets'][$block_data['tracknumber']])) { 
  1032. $info['matroska']['track_data_offsets'][$block_data['tracknumber']]['offset'] = $this->current_offset; 
  1033. $info['matroska']['track_data_offsets'][$block_data['tracknumber']]['length'] = $element['end'] - $this->current_offset; 
  1034. //$info['matroska']['track_data_offsets'][$block_data['tracknumber']]['total_length'] = 0; 
  1035. //$info['matroska']['track_data_offsets'][$block_data['tracknumber']]['total_length'] += $info['matroska']['track_data_offsets'][$block_data['tracknumber']]['length']; 
  1036. //$info['matroska']['track_data_offsets'][$block_data['tracknumber']]['duration'] = $block_data['timecode'] * ((isset($info['matroska']['info'][0]['TimecodeScale']) ? $info['matroska']['info'][0]['TimecodeScale'] : 1000000) / 1000000000); 
  1037.  
  1038. // set offset manually 
  1039. $this->current_offset = $element['end']; 
  1040.  
  1041. return $block_data; 
  1042.  
  1043. private static function EBML2Int($EBMLstring) { 
  1044. // http://matroska.org/specs/ 
  1045.  
  1046. // Element ID coded with an UTF-8 like system: 
  1047. // 1xxx xxxx - Class A IDs (2^7 -2 possible values) (base 0x8X) 
  1048. // 01xx xxxx xxxx xxxx - Class B IDs (2^14-2 possible values) (base 0x4X 0xXX) 
  1049. // 001x xxxx xxxx xxxx xxxx xxxx - Class C IDs (2^21-2 possible values) (base 0x2X 0xXX 0xXX) 
  1050. // 0001 xxxx xxxx xxxx xxxx xxxx xxxx xxxx - Class D IDs (2^28-2 possible values) (base 0x1X 0xXX 0xXX 0xXX) 
  1051. // Values with all x at 0 and 1 are reserved (hence the -2). 
  1052.  
  1053. // Data size, in octets, is also coded with an UTF-8 like system : 
  1054. // 1xxx xxxx - value 0 to 2^7-2 
  1055. // 01xx xxxx xxxx xxxx - value 0 to 2^14-2 
  1056. // 001x xxxx xxxx xxxx xxxx xxxx - value 0 to 2^21-2 
  1057. // 0001 xxxx xxxx xxxx xxxx xxxx xxxx xxxx - value 0 to 2^28-2 
  1058. // 0000 1xxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx - value 0 to 2^35-2 
  1059. // 0000 01xx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx - value 0 to 2^42-2 
  1060. // 0000 001x xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx - value 0 to 2^49-2 
  1061. // 0000 0001 xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx - value 0 to 2^56-2 
  1062.  
  1063. $first_byte_int = ord($EBMLstring[0]); 
  1064. if (0x80 & $first_byte_int) { 
  1065. $EBMLstring[0] = chr($first_byte_int & 0x7F); 
  1066. } elseif (0x40 & $first_byte_int) { 
  1067. $EBMLstring[0] = chr($first_byte_int & 0x3F); 
  1068. } elseif (0x20 & $first_byte_int) { 
  1069. $EBMLstring[0] = chr($first_byte_int & 0x1F); 
  1070. } elseif (0x10 & $first_byte_int) { 
  1071. $EBMLstring[0] = chr($first_byte_int & 0x0F); 
  1072. } elseif (0x08 & $first_byte_int) { 
  1073. $EBMLstring[0] = chr($first_byte_int & 0x07); 
  1074. } elseif (0x04 & $first_byte_int) { 
  1075. $EBMLstring[0] = chr($first_byte_int & 0x03); 
  1076. } elseif (0x02 & $first_byte_int) { 
  1077. $EBMLstring[0] = chr($first_byte_int & 0x01); 
  1078. } elseif (0x01 & $first_byte_int) { 
  1079. $EBMLstring[0] = chr($first_byte_int & 0x00); 
  1080.  
  1081. return getid3_lib::BigEndian2Int($EBMLstring); 
  1082.  
  1083. private static function EBMLdate2unix($EBMLdatestamp) { 
  1084. // Date - signed 8 octets integer in nanoseconds with 0 indicating the precise beginning of the millennium (at 2001-01-01T00:00:00, 000000000 UTC) 
  1085. // 978307200 == mktime(0, 0, 0, 1, 1, 2001) == January 1, 2001 12:00:00am UTC 
  1086. return round(($EBMLdatestamp / 1000000000) + 978307200); 
  1087.  
  1088. public static function TargetTypeValue($target_type) { 
  1089. // http://www.matroska.org/technical/specs/tagging/index.html 
  1090. static $TargetTypeValue = array(); 
  1091. if (empty($TargetTypeValue)) { 
  1092. $TargetTypeValue[10] = 'A: ~ V:shot'; // the lowest hierarchy found in music or movies 
  1093. $TargetTypeValue[20] = 'A:subtrack/part/movement ~ V:scene'; // corresponds to parts of a track for audio (like a movement) 
  1094. $TargetTypeValue[30] = 'A:track/song ~ V:chapter'; // the common parts of an album or a movie 
  1095. $TargetTypeValue[40] = 'A:part/session ~ V:part/session'; // when an album or episode has different logical parts 
  1096. $TargetTypeValue[50] = 'A:album/opera/concert ~ V:movie/episode/concert'; // the most common grouping level of music and video (equals to an episode for TV series) 
  1097. $TargetTypeValue[60] = 'A:edition/issue/volume/opus ~ V:season/sequel/volume'; // a list of lower levels grouped together 
  1098. $TargetTypeValue[70] = 'A:collection ~ V:collection'; // the high hierarchy consisting of many different lower items 
  1099. return (isset($TargetTypeValue[$target_type]) ? $TargetTypeValue[$target_type] : $target_type); 
  1100.  
  1101. public static function BlockLacingType($lacingtype) { 
  1102. // http://matroska.org/technical/specs/index.html#block_structure 
  1103. static $BlockLacingType = array(); 
  1104. if (empty($BlockLacingType)) { 
  1105. $BlockLacingType[0x00] = 'no lacing'; 
  1106. $BlockLacingType[0x01] = 'Xiph lacing'; 
  1107. $BlockLacingType[0x02] = 'fixed-size lacing'; 
  1108. $BlockLacingType[0x03] = 'EBML lacing'; 
  1109. return (isset($BlockLacingType[$lacingtype]) ? $BlockLacingType[$lacingtype] : $lacingtype); 
  1110.  
  1111. public static function CodecIDtoCommonName($codecid) { 
  1112. // http://www.matroska.org/technical/specs/codecid/index.html 
  1113. static $CodecIDlist = array(); 
  1114. if (empty($CodecIDlist)) { 
  1115. $CodecIDlist['A_AAC'] = 'aac'; 
  1116. $CodecIDlist['A_AAC/MPEG2/LC'] = 'aac'; 
  1117. $CodecIDlist['A_AC3'] = 'ac3'; 
  1118. $CodecIDlist['A_DTS'] = 'dts'; 
  1119. $CodecIDlist['A_FLAC'] = 'flac'; 
  1120. $CodecIDlist['A_MPEG/L1'] = 'mp1'; 
  1121. $CodecIDlist['A_MPEG/L2'] = 'mp2'; 
  1122. $CodecIDlist['A_MPEG/L3'] = 'mp3'; 
  1123. $CodecIDlist['A_PCM/INT/LIT'] = 'pcm'; // PCM Integer Little Endian 
  1124. $CodecIDlist['A_PCM/INT/BIG'] = 'pcm'; // PCM Integer Big Endian 
  1125. $CodecIDlist['A_QUICKTIME/QDMC'] = 'quicktime'; // Quicktime: QDesign Music 
  1126. $CodecIDlist['A_QUICKTIME/QDM2'] = 'quicktime'; // Quicktime: QDesign Music v2 
  1127. $CodecIDlist['A_VORBIS'] = 'vorbis'; 
  1128. $CodecIDlist['V_MPEG1'] = 'mpeg'; 
  1129. $CodecIDlist['V_THEORA'] = 'theora'; 
  1130. $CodecIDlist['V_REAL/RV40'] = 'real'; 
  1131. $CodecIDlist['V_REAL/RV10'] = 'real'; 
  1132. $CodecIDlist['V_REAL/RV20'] = 'real'; 
  1133. $CodecIDlist['V_REAL/RV30'] = 'real'; 
  1134. $CodecIDlist['V_QUICKTIME'] = 'quicktime'; // Quicktime 
  1135. $CodecIDlist['V_MPEG4/ISO/AP'] = 'mpeg4'; 
  1136. $CodecIDlist['V_MPEG4/ISO/ASP'] = 'mpeg4'; 
  1137. $CodecIDlist['V_MPEG4/ISO/AVC'] = 'h264'; 
  1138. $CodecIDlist['V_MPEG4/ISO/SP'] = 'mpeg4'; 
  1139. $CodecIDlist['V_VP8'] = 'vp8'; 
  1140. $CodecIDlist['V_MS/VFW/FOURCC'] = 'vcm'; // Microsoft (TM) Video Codec Manager (VCM) 
  1141. $CodecIDlist['A_MS/ACM'] = 'acm'; // Microsoft (TM) Audio Codec Manager (ACM) 
  1142. return (isset($CodecIDlist[$codecid]) ? $CodecIDlist[$codecid] : $codecid); 
  1143.  
  1144. private static function EBMLidName($value) { 
  1145. static $EBMLidList = array(); 
  1146. if (empty($EBMLidList)) { 
  1147. $EBMLidList[EBML_ID_ASPECTRATIOTYPE] = 'AspectRatioType'; 
  1148. $EBMLidList[EBML_ID_ATTACHEDFILE] = 'AttachedFile'; 
  1149. $EBMLidList[EBML_ID_ATTACHMENTLINK] = 'AttachmentLink'; 
  1150. $EBMLidList[EBML_ID_ATTACHMENTS] = 'Attachments'; 
  1151. $EBMLidList[EBML_ID_AUDIO] = 'Audio'; 
  1152. $EBMLidList[EBML_ID_BITDEPTH] = 'BitDepth'; 
  1153. $EBMLidList[EBML_ID_CHANNELPOSITIONS] = 'ChannelPositions'; 
  1154. $EBMLidList[EBML_ID_CHANNELS] = 'Channels'; 
  1155. $EBMLidList[EBML_ID_CHAPCOUNTRY] = 'ChapCountry'; 
  1156. $EBMLidList[EBML_ID_CHAPLANGUAGE] = 'ChapLanguage'; 
  1157. $EBMLidList[EBML_ID_CHAPPROCESS] = 'ChapProcess'; 
  1158. $EBMLidList[EBML_ID_CHAPPROCESSCODECID] = 'ChapProcessCodecID'; 
  1159. $EBMLidList[EBML_ID_CHAPPROCESSCOMMAND] = 'ChapProcessCommand'; 
  1160. $EBMLidList[EBML_ID_CHAPPROCESSDATA] = 'ChapProcessData'; 
  1161. $EBMLidList[EBML_ID_CHAPPROCESSPRIVATE] = 'ChapProcessPrivate'; 
  1162. $EBMLidList[EBML_ID_CHAPPROCESSTIME] = 'ChapProcessTime'; 
  1163. $EBMLidList[EBML_ID_CHAPSTRING] = 'ChapString'; 
  1164. $EBMLidList[EBML_ID_CHAPTERATOM] = 'ChapterAtom'; 
  1165. $EBMLidList[EBML_ID_CHAPTERDISPLAY] = 'ChapterDisplay'; 
  1166. $EBMLidList[EBML_ID_CHAPTERFLAGENABLED] = 'ChapterFlagEnabled'; 
  1167. $EBMLidList[EBML_ID_CHAPTERFLAGHIDDEN] = 'ChapterFlagHidden'; 
  1168. $EBMLidList[EBML_ID_CHAPTERPHYSICALEQUIV] = 'ChapterPhysicalEquiv'; 
  1169. $EBMLidList[EBML_ID_CHAPTERS] = 'Chapters'; 
  1170. $EBMLidList[EBML_ID_CHAPTERSEGMENTEDITIONUID] = 'ChapterSegmentEditionUID'; 
  1171. $EBMLidList[EBML_ID_CHAPTERSEGMENTUID] = 'ChapterSegmentUID'; 
  1172. $EBMLidList[EBML_ID_CHAPTERTIMEEND] = 'ChapterTimeEnd'; 
  1173. $EBMLidList[EBML_ID_CHAPTERTIMESTART] = 'ChapterTimeStart'; 
  1174. $EBMLidList[EBML_ID_CHAPTERTRACK] = 'ChapterTrack'; 
  1175. $EBMLidList[EBML_ID_CHAPTERTRACKNUMBER] = 'ChapterTrackNumber'; 
  1176. $EBMLidList[EBML_ID_CHAPTERTRANSLATE] = 'ChapterTranslate'; 
  1177. $EBMLidList[EBML_ID_CHAPTERTRANSLATECODEC] = 'ChapterTranslateCodec'; 
  1178. $EBMLidList[EBML_ID_CHAPTERTRANSLATEEDITIONUID] = 'ChapterTranslateEditionUID'; 
  1179. $EBMLidList[EBML_ID_CHAPTERTRANSLATEID] = 'ChapterTranslateID'; 
  1180. $EBMLidList[EBML_ID_CHAPTERUID] = 'ChapterUID'; 
  1181. $EBMLidList[EBML_ID_CLUSTER] = 'Cluster'; 
  1182. $EBMLidList[EBML_ID_CLUSTERBLOCK] = 'ClusterBlock'; 
  1183. $EBMLidList[EBML_ID_CLUSTERBLOCKADDID] = 'ClusterBlockAddID'; 
  1184. $EBMLidList[EBML_ID_CLUSTERBLOCKADDITIONAL] = 'ClusterBlockAdditional'; 
  1185. $EBMLidList[EBML_ID_CLUSTERBLOCKADDITIONID] = 'ClusterBlockAdditionID'; 
  1186. $EBMLidList[EBML_ID_CLUSTERBLOCKADDITIONS] = 'ClusterBlockAdditions'; 
  1187. $EBMLidList[EBML_ID_CLUSTERBLOCKDURATION] = 'ClusterBlockDuration'; 
  1188. $EBMLidList[EBML_ID_CLUSTERBLOCKGROUP] = 'ClusterBlockGroup'; 
  1189. $EBMLidList[EBML_ID_CLUSTERBLOCKMORE] = 'ClusterBlockMore'; 
  1190. $EBMLidList[EBML_ID_CLUSTERBLOCKVIRTUAL] = 'ClusterBlockVirtual'; 
  1191. $EBMLidList[EBML_ID_CLUSTERCODECSTATE] = 'ClusterCodecState'; 
  1192. $EBMLidList[EBML_ID_CLUSTERDELAY] = 'ClusterDelay'; 
  1193. $EBMLidList[EBML_ID_CLUSTERDURATION] = 'ClusterDuration'; 
  1194. $EBMLidList[EBML_ID_CLUSTERENCRYPTEDBLOCK] = 'ClusterEncryptedBlock'; 
  1195. $EBMLidList[EBML_ID_CLUSTERFRAMENUMBER] = 'ClusterFrameNumber'; 
  1196. $EBMLidList[EBML_ID_CLUSTERLACENUMBER] = 'ClusterLaceNumber'; 
  1197. $EBMLidList[EBML_ID_CLUSTERPOSITION] = 'ClusterPosition'; 
  1198. $EBMLidList[EBML_ID_CLUSTERPREVSIZE] = 'ClusterPrevSize'; 
  1199. $EBMLidList[EBML_ID_CLUSTERREFERENCEBLOCK] = 'ClusterReferenceBlock'; 
  1200. $EBMLidList[EBML_ID_CLUSTERREFERENCEPRIORITY] = 'ClusterReferencePriority'; 
  1201. $EBMLidList[EBML_ID_CLUSTERREFERENCEVIRTUAL] = 'ClusterReferenceVirtual'; 
  1202. $EBMLidList[EBML_ID_CLUSTERSILENTTRACKNUMBER] = 'ClusterSilentTrackNumber'; 
  1203. $EBMLidList[EBML_ID_CLUSTERSILENTTRACKS] = 'ClusterSilentTracks'; 
  1204. $EBMLidList[EBML_ID_CLUSTERSIMPLEBLOCK] = 'ClusterSimpleBlock'; 
  1205. $EBMLidList[EBML_ID_CLUSTERTIMECODE] = 'ClusterTimecode'; 
  1206. $EBMLidList[EBML_ID_CLUSTERTIMESLICE] = 'ClusterTimeSlice'; 
  1207. $EBMLidList[EBML_ID_CODECDECODEALL] = 'CodecDecodeAll'; 
  1208. $EBMLidList[EBML_ID_CODECDOWNLOADURL] = 'CodecDownloadURL'; 
  1209. $EBMLidList[EBML_ID_CODECID] = 'CodecID'; 
  1210. $EBMLidList[EBML_ID_CODECINFOURL] = 'CodecInfoURL'; 
  1211. $EBMLidList[EBML_ID_CODECNAME] = 'CodecName'; 
  1212. $EBMLidList[EBML_ID_CODECPRIVATE] = 'CodecPrivate'; 
  1213. $EBMLidList[EBML_ID_CODECSETTINGS] = 'CodecSettings'; 
  1214. $EBMLidList[EBML_ID_COLOURSPACE] = 'ColourSpace'; 
  1215. $EBMLidList[EBML_ID_CONTENTCOMPALGO] = 'ContentCompAlgo'; 
  1216. $EBMLidList[EBML_ID_CONTENTCOMPRESSION] = 'ContentCompression'; 
  1217. $EBMLidList[EBML_ID_CONTENTCOMPSETTINGS] = 'ContentCompSettings'; 
  1218. $EBMLidList[EBML_ID_CONTENTENCALGO] = 'ContentEncAlgo'; 
  1219. $EBMLidList[EBML_ID_CONTENTENCKEYID] = 'ContentEncKeyID'; 
  1220. $EBMLidList[EBML_ID_CONTENTENCODING] = 'ContentEncoding'; 
  1221. $EBMLidList[EBML_ID_CONTENTENCODINGORDER] = 'ContentEncodingOrder'; 
  1222. $EBMLidList[EBML_ID_CONTENTENCODINGS] = 'ContentEncodings'; 
  1223. $EBMLidList[EBML_ID_CONTENTENCODINGSCOPE] = 'ContentEncodingScope'; 
  1224. $EBMLidList[EBML_ID_CONTENTENCODINGTYPE] = 'ContentEncodingType'; 
  1225. $EBMLidList[EBML_ID_CONTENTENCRYPTION] = 'ContentEncryption'; 
  1226. $EBMLidList[EBML_ID_CONTENTSIGALGO] = 'ContentSigAlgo'; 
  1227. $EBMLidList[EBML_ID_CONTENTSIGHASHALGO] = 'ContentSigHashAlgo'; 
  1228. $EBMLidList[EBML_ID_CONTENTSIGKEYID] = 'ContentSigKeyID'; 
  1229. $EBMLidList[EBML_ID_CONTENTSIGNATURE] = 'ContentSignature'; 
  1230. $EBMLidList[EBML_ID_CRC32] = 'CRC32'; 
  1231. $EBMLidList[EBML_ID_CUEBLOCKNUMBER] = 'CueBlockNumber'; 
  1232. $EBMLidList[EBML_ID_CUECLUSTERPOSITION] = 'CueClusterPosition'; 
  1233. $EBMLidList[EBML_ID_CUECODECSTATE] = 'CueCodecState'; 
  1234. $EBMLidList[EBML_ID_CUEPOINT] = 'CuePoint'; 
  1235. $EBMLidList[EBML_ID_CUEREFCLUSTER] = 'CueRefCluster'; 
  1236. $EBMLidList[EBML_ID_CUEREFCODECSTATE] = 'CueRefCodecState'; 
  1237. $EBMLidList[EBML_ID_CUEREFERENCE] = 'CueReference'; 
  1238. $EBMLidList[EBML_ID_CUEREFNUMBER] = 'CueRefNumber'; 
  1239. $EBMLidList[EBML_ID_CUEREFTIME] = 'CueRefTime'; 
  1240. $EBMLidList[EBML_ID_CUES] = 'Cues'; 
  1241. $EBMLidList[EBML_ID_CUETIME] = 'CueTime'; 
  1242. $EBMLidList[EBML_ID_CUETRACK] = 'CueTrack'; 
  1243. $EBMLidList[EBML_ID_CUETRACKPOSITIONS] = 'CueTrackPositions'; 
  1244. $EBMLidList[EBML_ID_DATEUTC] = 'DateUTC'; 
  1245. $EBMLidList[EBML_ID_DEFAULTDURATION] = 'DefaultDuration'; 
  1246. $EBMLidList[EBML_ID_DISPLAYHEIGHT] = 'DisplayHeight'; 
  1247. $EBMLidList[EBML_ID_DISPLAYUNIT] = 'DisplayUnit'; 
  1248. $EBMLidList[EBML_ID_DISPLAYWIDTH] = 'DisplayWidth'; 
  1249. $EBMLidList[EBML_ID_DOCTYPE] = 'DocType'; 
  1250. $EBMLidList[EBML_ID_DOCTYPEREADVERSION] = 'DocTypeReadVersion'; 
  1251. $EBMLidList[EBML_ID_DOCTYPEVERSION] = 'DocTypeVersion'; 
  1252. $EBMLidList[EBML_ID_DURATION] = 'Duration'; 
  1253. $EBMLidList[EBML_ID_EBML] = 'EBML'; 
  1254. $EBMLidList[EBML_ID_EBMLMAXIDLENGTH] = 'EBMLMaxIDLength'; 
  1255. $EBMLidList[EBML_ID_EBMLMAXSIZELENGTH] = 'EBMLMaxSizeLength'; 
  1256. $EBMLidList[EBML_ID_EBMLREADVERSION] = 'EBMLReadVersion'; 
  1257. $EBMLidList[EBML_ID_EBMLVERSION] = 'EBMLVersion'; 
  1258. $EBMLidList[EBML_ID_EDITIONENTRY] = 'EditionEntry'; 
  1259. $EBMLidList[EBML_ID_EDITIONFLAGDEFAULT] = 'EditionFlagDefault'; 
  1260. $EBMLidList[EBML_ID_EDITIONFLAGHIDDEN] = 'EditionFlagHidden'; 
  1261. $EBMLidList[EBML_ID_EDITIONFLAGORDERED] = 'EditionFlagOrdered'; 
  1262. $EBMLidList[EBML_ID_EDITIONUID] = 'EditionUID'; 
  1263. $EBMLidList[EBML_ID_FILEDATA] = 'FileData'; 
  1264. $EBMLidList[EBML_ID_FILEDESCRIPTION] = 'FileDescription'; 
  1265. $EBMLidList[EBML_ID_FILEMIMETYPE] = 'FileMimeType'; 
  1266. $EBMLidList[EBML_ID_FILENAME] = 'FileName'; 
  1267. $EBMLidList[EBML_ID_FILEREFERRAL] = 'FileReferral'; 
  1268. $EBMLidList[EBML_ID_FILEUID] = 'FileUID'; 
  1269. $EBMLidList[EBML_ID_FLAGDEFAULT] = 'FlagDefault'; 
  1270. $EBMLidList[EBML_ID_FLAGENABLED] = 'FlagEnabled'; 
  1271. $EBMLidList[EBML_ID_FLAGFORCED] = 'FlagForced'; 
  1272. $EBMLidList[EBML_ID_FLAGINTERLACED] = 'FlagInterlaced'; 
  1273. $EBMLidList[EBML_ID_FLAGLACING] = 'FlagLacing'; 
  1274. $EBMLidList[EBML_ID_GAMMAVALUE] = 'GammaValue'; 
  1275. $EBMLidList[EBML_ID_INFO] = 'Info'; 
  1276. $EBMLidList[EBML_ID_LANGUAGE] = 'Language'; 
  1277. $EBMLidList[EBML_ID_MAXBLOCKADDITIONID] = 'MaxBlockAdditionID'; 
  1278. $EBMLidList[EBML_ID_MAXCACHE] = 'MaxCache'; 
  1279. $EBMLidList[EBML_ID_MINCACHE] = 'MinCache'; 
  1280. $EBMLidList[EBML_ID_MUXINGAPP] = 'MuxingApp'; 
  1281. $EBMLidList[EBML_ID_NAME] = 'Name'; 
  1282. $EBMLidList[EBML_ID_NEXTFILENAME] = 'NextFilename'; 
  1283. $EBMLidList[EBML_ID_NEXTUID] = 'NextUID'; 
  1284. $EBMLidList[EBML_ID_OUTPUTSAMPLINGFREQUENCY] = 'OutputSamplingFrequency'; 
  1285. $EBMLidList[EBML_ID_PIXELCROPBOTTOM] = 'PixelCropBottom'; 
  1286. $EBMLidList[EBML_ID_PIXELCROPLEFT] = 'PixelCropLeft'; 
  1287. $EBMLidList[EBML_ID_PIXELCROPRIGHT] = 'PixelCropRight'; 
  1288. $EBMLidList[EBML_ID_PIXELCROPTOP] = 'PixelCropTop'; 
  1289. $EBMLidList[EBML_ID_PIXELHEIGHT] = 'PixelHeight'; 
  1290. $EBMLidList[EBML_ID_PIXELWIDTH] = 'PixelWidth'; 
  1291. $EBMLidList[EBML_ID_PREVFILENAME] = 'PrevFilename'; 
  1292. $EBMLidList[EBML_ID_PREVUID] = 'PrevUID'; 
  1293. $EBMLidList[EBML_ID_SAMPLINGFREQUENCY] = 'SamplingFrequency'; 
  1294. $EBMLidList[EBML_ID_SEEK] = 'Seek'; 
  1295. $EBMLidList[EBML_ID_SEEKHEAD] = 'SeekHead'; 
  1296. $EBMLidList[EBML_ID_SEEKID] = 'SeekID'; 
  1297. $EBMLidList[EBML_ID_SEEKPOSITION] = 'SeekPosition'; 
  1298. $EBMLidList[EBML_ID_SEGMENT] = 'Segment'; 
  1299. $EBMLidList[EBML_ID_SEGMENTFAMILY] = 'SegmentFamily'; 
  1300. $EBMLidList[EBML_ID_SEGMENTFILENAME] = 'SegmentFilename'; 
  1301. $EBMLidList[EBML_ID_SEGMENTUID] = 'SegmentUID'; 
  1302. $EBMLidList[EBML_ID_SIMPLETAG] = 'SimpleTag'; 
  1303. $EBMLidList[EBML_ID_CLUSTERSLICES] = 'ClusterSlices'; 
  1304. $EBMLidList[EBML_ID_STEREOMODE] = 'StereoMode'; 
  1305. $EBMLidList[EBML_ID_OLDSTEREOMODE] = 'OldStereoMode'; 
  1306. $EBMLidList[EBML_ID_TAG] = 'Tag'; 
  1307. $EBMLidList[EBML_ID_TAGATTACHMENTUID] = 'TagAttachmentUID'; 
  1308. $EBMLidList[EBML_ID_TAGBINARY] = 'TagBinary'; 
  1309. $EBMLidList[EBML_ID_TAGCHAPTERUID] = 'TagChapterUID'; 
  1310. $EBMLidList[EBML_ID_TAGDEFAULT] = 'TagDefault'; 
  1311. $EBMLidList[EBML_ID_TAGEDITIONUID] = 'TagEditionUID'; 
  1312. $EBMLidList[EBML_ID_TAGLANGUAGE] = 'TagLanguage'; 
  1313. $EBMLidList[EBML_ID_TAGNAME] = 'TagName'; 
  1314. $EBMLidList[EBML_ID_TAGTRACKUID] = 'TagTrackUID'; 
  1315. $EBMLidList[EBML_ID_TAGS] = 'Tags'; 
  1316. $EBMLidList[EBML_ID_TAGSTRING] = 'TagString'; 
  1317. $EBMLidList[EBML_ID_TARGETS] = 'Targets'; 
  1318. $EBMLidList[EBML_ID_TARGETTYPE] = 'TargetType'; 
  1319. $EBMLidList[EBML_ID_TARGETTYPEVALUE] = 'TargetTypeValue'; 
  1320. $EBMLidList[EBML_ID_TIMECODESCALE] = 'TimecodeScale'; 
  1321. $EBMLidList[EBML_ID_TITLE] = 'Title'; 
  1322. $EBMLidList[EBML_ID_TRACKENTRY] = 'TrackEntry'; 
  1323. $EBMLidList[EBML_ID_TRACKNUMBER] = 'TrackNumber'; 
  1324. $EBMLidList[EBML_ID_TRACKOFFSET] = 'TrackOffset'; 
  1325. $EBMLidList[EBML_ID_TRACKOVERLAY] = 'TrackOverlay'; 
  1326. $EBMLidList[EBML_ID_TRACKS] = 'Tracks'; 
  1327. $EBMLidList[EBML_ID_TRACKTIMECODESCALE] = 'TrackTimecodeScale'; 
  1328. $EBMLidList[EBML_ID_TRACKTRANSLATE] = 'TrackTranslate'; 
  1329. $EBMLidList[EBML_ID_TRACKTRANSLATECODEC] = 'TrackTranslateCodec'; 
  1330. $EBMLidList[EBML_ID_TRACKTRANSLATEEDITIONUID] = 'TrackTranslateEditionUID'; 
  1331. $EBMLidList[EBML_ID_TRACKTRANSLATETRACKID] = 'TrackTranslateTrackID'; 
  1332. $EBMLidList[EBML_ID_TRACKTYPE] = 'TrackType'; 
  1333. $EBMLidList[EBML_ID_TRACKUID] = 'TrackUID'; 
  1334. $EBMLidList[EBML_ID_VIDEO] = 'Video'; 
  1335. $EBMLidList[EBML_ID_VOID] = 'Void'; 
  1336. $EBMLidList[EBML_ID_WRITINGAPP] = 'WritingApp'; 
  1337.  
  1338. return (isset($EBMLidList[$value]) ? $EBMLidList[$value] : dechex($value)); 
  1339.  
  1340. public static function displayUnit($value) { 
  1341. // http://www.matroska.org/technical/specs/index.html#DisplayUnit 
  1342. static $units = array( 
  1343. 0 => 'pixels',  
  1344. 1 => 'centimeters',  
  1345. 2 => 'inches',  
  1346. 3 => 'Display Aspect Ratio'); 
  1347.  
  1348. return (isset($units[$value]) ? $units[$value] : 'unknown'); 
  1349.  
  1350. private static function getDefaultStreamInfo($streams) 
  1351. foreach (array_reverse($streams) as $stream) { 
  1352. if ($stream['default']) { 
  1353. break; 
  1354.  
  1355. $unset = array('default', 'name'); 
  1356. foreach ($unset as $u) { 
  1357. if (isset($stream[$u])) { 
  1358. unset($stream[$u]); 
  1359.  
  1360. $info = $stream; 
  1361. $info['streams'] = $streams; 
  1362.  
  1363. return $info; 
  1364.