PageRenderTime 62ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/wp-includes/ID3/module.audio-video.quicktime.php

https://gitlab.com/Gashler/dp
PHP | 1006 lines | 802 code | 131 blank | 73 comment | 96 complexity | d9d85250980387007a163cc7ad7e4886 MD5 | raw file
  1. <?php
  2. /////////////////////////////////////////////////////////////////
  3. /// getID3() by James Heinrich <info@getid3.org> //
  4. // available at http://getid3.sourceforge.net //
  5. // or http://www.getid3.org //
  6. /////////////////////////////////////////////////////////////////
  7. // See readme.txt for more details //
  8. /////////////////////////////////////////////////////////////////
  9. // //
  10. // module.audio-video.quicktime.php //
  11. // module for analyzing Quicktime and MP3-in-MP4 files //
  12. // dependencies: module.audio.mp3.php //
  13. // dependencies: module.tag.id3v2.php //
  14. // ///
  15. /////////////////////////////////////////////////////////////////
  16. getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.mp3.php', __FILE__, true);
  17. getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.id3v2.php', __FILE__, true); // needed for ISO 639-2 language code lookup
  18. class getid3_quicktime extends getid3_handler
  19. {
  20. public $ReturnAtomData = true;
  21. public $ParseAllPossibleAtoms = false;
  22. public function Analyze() {
  23. $info = &$this->getid3->info;
  24. $info['fileformat'] = 'quicktime';
  25. $info['quicktime']['hinting'] = false;
  26. $info['quicktime']['controller'] = 'standard'; // may be overridden if 'ctyp' atom is present
  27. fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET);
  28. $offset = 0;
  29. $atomcounter = 0;
  30. while ($offset < $info['avdataend']) {
  31. if (!getid3_lib::intValueSupported($offset)) {
  32. $info['error'][] = 'Unable to parse atom at offset '.$offset.' because beyond '.round(PHP_INT_MAX / 1073741824).'GB limit of PHP filesystem functions';
  33. break;
  34. }
  35. fseek($this->getid3->fp, $offset, SEEK_SET);
  36. $AtomHeader = fread($this->getid3->fp, 8);
  37. $atomsize = getid3_lib::BigEndian2Int(substr($AtomHeader, 0, 4));
  38. $atomname = substr($AtomHeader, 4, 4);
  39. // 64-bit MOV patch by jlegateØktnc*com
  40. if ($atomsize == 1) {
  41. $atomsize = getid3_lib::BigEndian2Int(fread($this->getid3->fp, 8));
  42. }
  43. $info['quicktime'][$atomname]['name'] = $atomname;
  44. $info['quicktime'][$atomname]['size'] = $atomsize;
  45. $info['quicktime'][$atomname]['offset'] = $offset;
  46. if (($offset + $atomsize) > $info['avdataend']) {
  47. $info['error'][] = 'Atom at offset '.$offset.' claims to go beyond end-of-file (length: '.$atomsize.' bytes)';
  48. return false;
  49. }
  50. if ($atomsize == 0) {
  51. // Furthermore, for historical reasons the list of atoms is optionally
  52. // terminated by a 32-bit integer set to 0. If you are writing a program
  53. // to read user data atoms, you should allow for the terminating 0.
  54. break;
  55. }
  56. switch ($atomname) {
  57. case 'mdat': // Media DATa atom
  58. // 'mdat' contains the actual data for the audio/video
  59. if (($atomsize > 8) && (!isset($info['avdataend_tmp']) || ($info['quicktime'][$atomname]['size'] > ($info['avdataend_tmp'] - $info['avdataoffset'])))) {
  60. $info['avdataoffset'] = $info['quicktime'][$atomname]['offset'] + 8;
  61. $OldAVDataEnd = $info['avdataend'];
  62. $info['avdataend'] = $info['quicktime'][$atomname]['offset'] + $info['quicktime'][$atomname]['size'];
  63. $getid3_temp = new getID3();
  64. $getid3_temp->openfile($this->getid3->filename);
  65. $getid3_temp->info['avdataoffset'] = $info['avdataoffset'];
  66. $getid3_temp->info['avdataend'] = $info['avdataend'];
  67. $getid3_mp3 = new getid3_mp3($getid3_temp);
  68. if ($getid3_mp3->MPEGaudioHeaderValid($getid3_mp3->MPEGaudioHeaderDecode(fread($this->getid3->fp, 4)))) {
  69. $getid3_mp3->getOnlyMPEGaudioInfo($getid3_temp->info['avdataoffset'], false);
  70. if (!empty($getid3_temp->info['warning'])) {
  71. foreach ($getid3_temp->info['warning'] as $value) {
  72. $info['warning'][] = $value;
  73. }
  74. }
  75. if (!empty($getid3_temp->info['mpeg'])) {
  76. $info['mpeg'] = $getid3_temp->info['mpeg'];
  77. if (isset($info['mpeg']['audio'])) {
  78. $info['audio']['dataformat'] = 'mp3';
  79. $info['audio']['codec'] = (!empty($info['mpeg']['audio']['encoder']) ? $info['mpeg']['audio']['encoder'] : (!empty($info['mpeg']['audio']['codec']) ? $info['mpeg']['audio']['codec'] : (!empty($info['mpeg']['audio']['LAME']) ? 'LAME' :'mp3')));
  80. $info['audio']['sample_rate'] = $info['mpeg']['audio']['sample_rate'];
  81. $info['audio']['channels'] = $info['mpeg']['audio']['channels'];
  82. $info['audio']['bitrate'] = $info['mpeg']['audio']['bitrate'];
  83. $info['audio']['bitrate_mode'] = strtolower($info['mpeg']['audio']['bitrate_mode']);
  84. $info['bitrate'] = $info['audio']['bitrate'];
  85. }
  86. }
  87. }
  88. unset($getid3_mp3, $getid3_temp);
  89. $info['avdataend'] = $OldAVDataEnd;
  90. unset($OldAVDataEnd);
  91. }
  92. break;
  93. case 'free': // FREE space atom
  94. case 'skip': // SKIP atom
  95. case 'wide': // 64-bit expansion placeholder atom
  96. // 'free', 'skip' and 'wide' are just padding, contains no useful data at all
  97. break;
  98. default:
  99. $atomHierarchy = array();
  100. $info['quicktime'][$atomname] = $this->QuicktimeParseAtom($atomname, $atomsize, fread($this->getid3->fp, $atomsize), $offset, $atomHierarchy, $this->ParseAllPossibleAtoms);
  101. break;
  102. }
  103. $offset += $atomsize;
  104. $atomcounter++;
  105. }
  106. if (!empty($info['avdataend_tmp'])) {
  107. // this value is assigned to a temp value and then erased because
  108. // otherwise any atoms beyond the 'mdat' atom would not get parsed
  109. $info['avdataend'] = $info['avdataend_tmp'];
  110. unset($info['avdataend_tmp']);
  111. }
  112. if (!isset($info['bitrate']) && isset($info['playtime_seconds'])) {
  113. $info['bitrate'] = (($info['avdataend'] - $info['avdataoffset']) * 8) / $info['playtime_seconds'];
  114. }
  115. if (isset($info['bitrate']) && !isset($info['audio']['bitrate']) && !isset($info['quicktime']['video'])) {
  116. $info['audio']['bitrate'] = $info['bitrate'];
  117. }
  118. if (!empty($info['playtime_seconds']) && !isset($info['video']['frame_rate']) && !empty($info['quicktime']['stts_framecount'])) {
  119. foreach ($info['quicktime']['stts_framecount'] as $key => $samples_count) {
  120. $samples_per_second = $samples_count / $info['playtime_seconds'];
  121. if ($samples_per_second > 240) {
  122. // has to be audio samples
  123. } else {
  124. $info['video']['frame_rate'] = $samples_per_second;
  125. break;
  126. }
  127. }
  128. }
  129. if (($info['audio']['dataformat'] == 'mp4') && empty($info['video']['resolution_x'])) {
  130. $info['fileformat'] = 'mp4';
  131. $info['mime_type'] = 'audio/mp4';
  132. unset($info['video']['dataformat']);
  133. }
  134. if (!$this->ReturnAtomData) {
  135. unset($info['quicktime']['moov']);
  136. }
  137. if (empty($info['audio']['dataformat']) && !empty($info['quicktime']['audio'])) {
  138. $info['audio']['dataformat'] = 'quicktime';
  139. }
  140. if (empty($info['video']['dataformat']) && !empty($info['quicktime']['video'])) {
  141. $info['video']['dataformat'] = 'quicktime';
  142. }
  143. return true;
  144. }
  145. public function QuicktimeParseAtom($atomname, $atomsize, $atom_data, $baseoffset, &$atomHierarchy, $ParseAllPossibleAtoms) {
  146. // http://developer.apple.com/techpubs/quicktime/qtdevdocs/APIREF/INDEX/atomalphaindex.htm
  147. $info = &$this->getid3->info;
  148. //$atom_parent = array_pop($atomHierarchy);
  149. $atom_parent = end($atomHierarchy); // http://www.getid3.org/phpBB3/viewtopic.php?t=1717
  150. array_push($atomHierarchy, $atomname);
  151. $atom_structure['hierarchy'] = implode(' ', $atomHierarchy);
  152. $atom_structure['name'] = $atomname;
  153. $atom_structure['size'] = $atomsize;
  154. $atom_structure['offset'] = $baseoffset;
  155. //echo getid3_lib::PrintHexBytes(substr($atom_data, 0, 8)).'<br>';
  156. //echo getid3_lib::PrintHexBytes(substr($atom_data, 0, 8), false).'<br><br>';
  157. switch ($atomname) {
  158. case 'moov': // MOVie container atom
  159. case 'trak': // TRAcK container atom
  160. case 'clip': // CLIPping container atom
  161. case 'matt': // track MATTe container atom
  162. case 'edts': // EDiTS container atom
  163. case 'tref': // Track REFerence container atom
  164. case 'mdia': // MeDIA container atom
  165. case 'minf': // Media INFormation container atom
  166. case 'dinf': // Data INFormation container atom
  167. case 'udta': // User DaTA container atom
  168. case 'cmov': // Compressed MOVie container atom
  169. case 'rmra': // Reference Movie Record Atom
  170. case 'rmda': // Reference Movie Descriptor Atom
  171. case 'gmhd': // Generic Media info HeaDer atom (seen on QTVR)
  172. $atom_structure['subatoms'] = $this->QuicktimeParseContainerAtom($atom_data, $baseoffset + 8, $atomHierarchy, $ParseAllPossibleAtoms);
  173. break;
  174. case 'ilst': // Item LiST container atom
  175. $atom_structure['subatoms'] = $this->QuicktimeParseContainerAtom($atom_data, $baseoffset + 8, $atomHierarchy, $ParseAllPossibleAtoms);
  176. // some "ilst" atoms contain data atoms that have a numeric name, and the data is far more accessible if the returned array is compacted
  177. $allnumericnames = true;
  178. foreach ($atom_structure['subatoms'] as $subatomarray) {
  179. if (!is_integer($subatomarray['name']) || (count($subatomarray['subatoms']) != 1)) {
  180. $allnumericnames = false;
  181. break;
  182. }
  183. }
  184. if ($allnumericnames) {
  185. $newData = array();
  186. foreach ($atom_structure['subatoms'] as $subatomarray) {
  187. foreach ($subatomarray['subatoms'] as $newData_subatomarray) {
  188. unset($newData_subatomarray['hierarchy'], $newData_subatomarray['name']);
  189. $newData[$subatomarray['name']] = $newData_subatomarray;
  190. break;
  191. }
  192. }
  193. $atom_structure['data'] = $newData;
  194. unset($atom_structure['subatoms']);
  195. }
  196. break;
  197. case "\x00\x00\x00\x01":
  198. case "\x00\x00\x00\x02":
  199. case "\x00\x00\x00\x03":
  200. case "\x00\x00\x00\x04":
  201. case "\x00\x00\x00\x05":
  202. $atomname = getid3_lib::BigEndian2Int($atomname);
  203. $atom_structure['name'] = $atomname;
  204. $atom_structure['subatoms'] = $this->QuicktimeParseContainerAtom($atom_data, $baseoffset + 8, $atomHierarchy, $ParseAllPossibleAtoms);
  205. break;
  206. case 'stbl': // Sample TaBLe container atom
  207. $atom_structure['subatoms'] = $this->QuicktimeParseContainerAtom($atom_data, $baseoffset + 8, $atomHierarchy, $ParseAllPossibleAtoms);
  208. $isVideo = false;
  209. $framerate = 0;
  210. $framecount = 0;
  211. foreach ($atom_structure['subatoms'] as $key => $value_array) {
  212. if (isset($value_array['sample_description_table'])) {
  213. foreach ($value_array['sample_description_table'] as $key2 => $value_array2) {
  214. if (isset($value_array2['data_format'])) {
  215. switch ($value_array2['data_format']) {
  216. case 'avc1':
  217. case 'mp4v':
  218. // video data
  219. $isVideo = true;
  220. break;
  221. case 'mp4a':
  222. // audio data
  223. break;
  224. }
  225. }
  226. }
  227. } elseif (isset($value_array['time_to_sample_table'])) {
  228. foreach ($value_array['time_to_sample_table'] as $key2 => $value_array2) {
  229. if (isset($value_array2['sample_count']) && isset($value_array2['sample_duration']) && ($value_array2['sample_duration'] > 0)) {
  230. $framerate = round($info['quicktime']['time_scale'] / $value_array2['sample_duration'], 3);
  231. $framecount = $value_array2['sample_count'];
  232. }
  233. }
  234. }
  235. }
  236. if ($isVideo && $framerate) {
  237. $info['quicktime']['video']['frame_rate'] = $framerate;
  238. $info['video']['frame_rate'] = $info['quicktime']['video']['frame_rate'];
  239. }
  240. if ($isVideo && $framecount) {
  241. $info['quicktime']['video']['frame_count'] = $framecount;
  242. }
  243. break;
  244. case 'aART': // Album ARTist
  245. case 'catg': // CaTeGory
  246. case 'covr': // COVeR artwork
  247. case 'cpil': // ComPILation
  248. case 'cprt': // CoPyRighT
  249. case 'desc': // DESCription
  250. case 'disk': // DISK number
  251. case 'egid': // Episode Global ID
  252. case 'gnre': // GeNRE
  253. case 'keyw': // KEYWord
  254. case 'ldes':
  255. case 'pcst': // PodCaST
  256. case 'pgap': // GAPless Playback
  257. case 'purd': // PURchase Date
  258. case 'purl': // Podcast URL
  259. case 'rati':
  260. case 'rndu':
  261. case 'rpdu':
  262. case 'rtng': // RaTiNG
  263. case 'stik':
  264. case 'tmpo': // TeMPO (BPM)
  265. case 'trkn': // TRacK Number
  266. case 'tves': // TV EpiSode
  267. case 'tvnn': // TV Network Name
  268. case 'tvsh': // TV SHow Name
  269. case 'tvsn': // TV SeasoN
  270. case 'akID': // iTunes store account type
  271. case 'apID':
  272. case 'atID':
  273. case 'cmID':
  274. case 'cnID':
  275. case 'geID':
  276. case 'plID':
  277. case 'sfID': // iTunes store country
  278. case '©alb': // ALBum
  279. case '©art': // ARTist
  280. case '©ART':
  281. case '©aut':
  282. case '©cmt': // CoMmenT
  283. case '©com': // COMposer
  284. case '©cpy':
  285. case '©day': // content created year
  286. case '©dir':
  287. case '©ed1':
  288. case '©ed2':
  289. case '©ed3':
  290. case '©ed4':
  291. case '©ed5':
  292. case '©ed6':
  293. case '©ed7':
  294. case '©ed8':
  295. case '©ed9':
  296. case '©enc':
  297. case '©fmt':
  298. case '©gen': // GENre
  299. case '©grp': // GRouPing
  300. case '©hst':
  301. case '©inf':
  302. case '©lyr': // LYRics
  303. case '©mak':
  304. case '©mod':
  305. case '©nam': // full NAMe
  306. case '©ope':
  307. case '©PRD':
  308. case '©prd':
  309. case '©prf':
  310. case '©req':
  311. case '©src':
  312. case '©swr':
  313. case '©too': // encoder
  314. case '©trk': // TRacK
  315. case '©url':
  316. case '©wrn':
  317. case '©wrt': // WRiTer
  318. case '----': // itunes specific
  319. if ($atom_parent == 'udta') {
  320. // User data atom handler
  321. $atom_structure['data_length'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 2));
  322. $atom_structure['language_id'] = getid3_lib::BigEndian2Int(substr($atom_data, 2, 2));
  323. $atom_structure['data'] = substr($atom_data, 4);
  324. $atom_structure['language'] = $this->QuicktimeLanguageLookup($atom_structure['language_id']);
  325. if (empty($info['comments']['language']) || (!in_array($atom_structure['language'], $info['comments']['language']))) {
  326. $info['comments']['language'][] = $atom_structure['language'];
  327. }
  328. } else {
  329. // Apple item list box atom handler
  330. $atomoffset = 0;
  331. if (substr($atom_data, 2, 2) == "\x10\xB5") {
  332. // not sure what it means, but observed on iPhone4 data.
  333. // Each $atom_data has 2 bytes of datasize, plus 0x10B5, then data
  334. while ($atomoffset < strlen($atom_data)) {
  335. $boxsmallsize = getid3_lib::BigEndian2Int(substr($atom_data, $atomoffset, 2));
  336. $boxsmalltype = substr($atom_data, $atomoffset + 2, 2);
  337. $boxsmalldata = substr($atom_data, $atomoffset + 4, $boxsmallsize);
  338. if ($boxsmallsize <= 1) {
  339. $info['warning'][] = 'Invalid QuickTime atom smallbox size "'.$boxsmallsize.'" in atom "'.$atomname.'" at offset: '.($atom_structure['offset'] + $atomoffset);
  340. $atom_structure['data'] = null;
  341. $atomoffset = strlen($atom_data);
  342. break;
  343. }
  344. switch ($boxsmalltype) {
  345. case "\x10\xB5":
  346. $atom_structure['data'] = $boxsmalldata;
  347. break;
  348. default:
  349. $info['warning'][] = 'Unknown QuickTime smallbox type: "'.getid3_lib::PrintHexBytes($boxsmalltype).'" at offset '.$baseoffset;
  350. $atom_structure['data'] = $atom_data;
  351. break;
  352. }
  353. $atomoffset += (4 + $boxsmallsize);
  354. }
  355. } else {
  356. while ($atomoffset < strlen($atom_data)) {
  357. $boxsize = getid3_lib::BigEndian2Int(substr($atom_data, $atomoffset, 4));
  358. $boxtype = substr($atom_data, $atomoffset + 4, 4);
  359. $boxdata = substr($atom_data, $atomoffset + 8, $boxsize - 8);
  360. if ($boxsize <= 1) {
  361. $info['warning'][] = 'Invalid QuickTime atom box size "'.$boxsize.'" in atom "'.$atomname.'" at offset: '.($atom_structure['offset'] + $atomoffset);
  362. $atom_structure['data'] = null;
  363. $atomoffset = strlen($atom_data);
  364. break;
  365. }
  366. $atomoffset += $boxsize;
  367. switch ($boxtype) {
  368. case 'mean':
  369. case 'name':
  370. $atom_structure[$boxtype] = substr($boxdata, 4);
  371. break;
  372. case 'data':
  373. $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($boxdata, 0, 1));
  374. $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($boxdata, 1, 3));
  375. switch ($atom_structure['flags_raw']) {
  376. case 0: // data flag
  377. case 21: // tmpo/cpil flag
  378. switch ($atomname) {
  379. case 'cpil':
  380. case 'pcst':
  381. case 'pgap':
  382. $atom_structure['data'] = getid3_lib::BigEndian2Int(substr($boxdata, 8, 1));
  383. break;
  384. case 'tmpo':
  385. $atom_structure['data'] = getid3_lib::BigEndian2Int(substr($boxdata, 8, 2));
  386. break;
  387. case 'disk':
  388. case 'trkn':
  389. $num = getid3_lib::BigEndian2Int(substr($boxdata, 10, 2));
  390. $num_total = getid3_lib::BigEndian2Int(substr($boxdata, 12, 2));
  391. $atom_structure['data'] = empty($num) ? '' : $num;
  392. $atom_structure['data'] .= empty($num_total) ? '' : '/'.$num_total;
  393. break;
  394. case 'gnre':
  395. $GenreID = getid3_lib::BigEndian2Int(substr($boxdata, 8, 4));
  396. $atom_structure['data'] = getid3_id3v1::LookupGenreName($GenreID - 1);
  397. break;
  398. case 'rtng':
  399. $atom_structure[$atomname] = getid3_lib::BigEndian2Int(substr($boxdata, 8, 1));
  400. $atom_structure['data'] = $this->QuicktimeContentRatingLookup($atom_structure[$atomname]);
  401. break;
  402. case 'stik':
  403. $atom_structure[$atomname] = getid3_lib::BigEndian2Int(substr($boxdata, 8, 1));
  404. $atom_structure['data'] = $this->QuicktimeSTIKLookup($atom_structure[$atomname]);
  405. break;
  406. case 'sfID':
  407. $atom_structure[$atomname] = getid3_lib::BigEndian2Int(substr($boxdata, 8, 4));
  408. $atom_structure['data'] = $this->QuicktimeStoreFrontCodeLookup($atom_structure[$atomname]);
  409. break;
  410. case 'egid':
  411. case 'purl':
  412. $atom_structure['data'] = substr($boxdata, 8);
  413. break;
  414. default:
  415. $atom_structure['data'] = getid3_lib::BigEndian2Int(substr($boxdata, 8, 4));
  416. }
  417. break;
  418. case 1: // text flag
  419. case 13: // image flag
  420. default:
  421. $atom_structure['data'] = substr($boxdata, 8);
  422. break;
  423. }
  424. break;
  425. default:
  426. $info['warning'][] = 'Unknown QuickTime box type: "'.getid3_lib::PrintHexBytes($boxtype).'" at offset '.$baseoffset;
  427. $atom_structure['data'] = $atom_data;
  428. }
  429. }
  430. }
  431. }
  432. $this->CopyToAppropriateCommentsSection($atomname, $atom_structure['data'], $atom_structure['name']);
  433. break;
  434. case 'play': // auto-PLAY atom
  435. $atom_structure['autoplay'] = (bool) getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
  436. $info['quicktime']['autoplay'] = $atom_structure['autoplay'];
  437. break;
  438. case 'WLOC': // Window LOCation atom
  439. $atom_structure['location_x'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 2));
  440. $atom_structure['location_y'] = getid3_lib::BigEndian2Int(substr($atom_data, 2, 2));
  441. break;
  442. case 'LOOP': // LOOPing atom
  443. case 'SelO': // play SELection Only atom
  444. case 'AllF': // play ALL Frames atom
  445. $atom_structure['data'] = getid3_lib::BigEndian2Int($atom_data);
  446. break;
  447. case 'name': //
  448. case 'MCPS': // Media Cleaner PRo
  449. case '@PRM': // adobe PReMiere version
  450. case '@PRQ': // adobe PRemiere Quicktime version
  451. $atom_structure['data'] = $atom_data;
  452. break;
  453. case 'cmvd': // Compressed MooV Data atom
  454. // Code by ubergeekØubergeek*tv based on information from
  455. // http://developer.apple.com/quicktime/icefloe/dispatch012.html
  456. $atom_structure['unCompressedSize'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 4));
  457. $CompressedFileData = substr($atom_data, 4);
  458. if ($UncompressedHeader = @gzuncompress($CompressedFileData)) {
  459. $atom_structure['subatoms'] = $this->QuicktimeParseContainerAtom($UncompressedHeader, 0, $atomHierarchy, $ParseAllPossibleAtoms);
  460. } else {
  461. $info['warning'][] = 'Error decompressing compressed MOV atom at offset '.$atom_structure['offset'];
  462. }
  463. break;
  464. case 'dcom': // Data COMpression atom
  465. $atom_structure['compression_id'] = $atom_data;
  466. $atom_structure['compression_text'] = $this->QuicktimeDCOMLookup($atom_data);
  467. break;
  468. case 'rdrf': // Reference movie Data ReFerence atom
  469. $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
  470. $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3));
  471. $atom_structure['flags']['internal_data'] = (bool) ($atom_structure['flags_raw'] & 0x000001);
  472. $atom_structure['reference_type_name'] = substr($atom_data, 4, 4);
  473. $atom_structure['reference_length'] = getid3_lib::BigEndian2Int(substr($atom_data, 8, 4));
  474. switch ($atom_structure['reference_type_name']) {
  475. case 'url ':
  476. $atom_structure['url'] = $this->NoNullString(substr($atom_data, 12));
  477. break;
  478. case 'alis':
  479. $atom_structure['file_alias'] = substr($atom_data, 12);
  480. break;
  481. case 'rsrc':
  482. $atom_structure['resource_alias'] = substr($atom_data, 12);
  483. break;
  484. default:
  485. $atom_structure['data'] = substr($atom_data, 12);
  486. break;
  487. }
  488. break;
  489. case 'rmqu': // Reference Movie QUality atom
  490. $atom_structure['movie_quality'] = getid3_lib::BigEndian2Int($atom_data);
  491. break;
  492. case 'rmcs': // Reference Movie Cpu Speed atom
  493. $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
  494. $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000
  495. $atom_structure['cpu_speed_rating'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 2));
  496. break;
  497. case 'rmvc': // Reference Movie Version Check atom
  498. $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
  499. $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000
  500. $atom_structure['gestalt_selector'] = substr($atom_data, 4, 4);
  501. $atom_structure['gestalt_value_mask'] = getid3_lib::BigEndian2Int(substr($atom_data, 8, 4));
  502. $atom_structure['gestalt_value'] = getid3_lib::BigEndian2Int(substr($atom_data, 12, 4));
  503. $atom_structure['gestalt_check_type'] = getid3_lib::BigEndian2Int(substr($atom_data, 14, 2));
  504. break;
  505. case 'rmcd': // Reference Movie Component check atom
  506. $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
  507. $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000
  508. $atom_structure['component_type'] = substr($atom_data, 4, 4);
  509. $atom_structure['component_subtype'] = substr($atom_data, 8, 4);
  510. $atom_structure['component_manufacturer'] = substr($atom_data, 12, 4);
  511. $atom_structure['component_flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 16, 4));
  512. $atom_structure['component_flags_mask'] = getid3_lib::BigEndian2Int(substr($atom_data, 20, 4));
  513. $atom_structure['component_min_version'] = getid3_lib::BigEndian2Int(substr($atom_data, 24, 4));
  514. break;
  515. case 'rmdr': // Reference Movie Data Rate atom
  516. $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
  517. $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000
  518. $atom_structure['data_rate'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4));
  519. $atom_structure['data_rate_bps'] = $atom_structure['data_rate'] * 10;
  520. break;
  521. case 'rmla': // Reference Movie Language Atom
  522. $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
  523. $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000
  524. $atom_structure['language_id'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 2));
  525. $atom_structure['language'] = $this->QuicktimeLanguageLookup($atom_structure['language_id']);
  526. if (empty($info['comments']['language']) || (!in_array($atom_structure['language'], $info['comments']['language']))) {
  527. $info['comments']['language'][] = $atom_structure['language'];
  528. }
  529. break;
  530. case 'rmla': // Reference Movie Language Atom
  531. $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
  532. $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000
  533. $atom_structure['track_id'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 2));
  534. break;
  535. case 'ptv ': // Print To Video - defines a movie's full screen mode
  536. // http://developer.apple.com/documentation/QuickTime/APIREF/SOURCESIV/at_ptv-_pg.htm
  537. $atom_structure['display_size_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 2));
  538. $atom_structure['reserved_1'] = getid3_lib::BigEndian2Int(substr($atom_data, 2, 2)); // hardcoded: 0x0000
  539. $atom_structure['reserved_2'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 2)); // hardcoded: 0x0000
  540. $atom_structure['slide_show_flag'] = getid3_lib::BigEndian2Int(substr($atom_data, 6, 1));
  541. $atom_structure['play_on_open_flag'] = getid3_lib::BigEndian2Int(substr($atom_data, 7, 1));
  542. $atom_structure['flags']['play_on_open'] = (bool) $atom_structure['play_on_open_flag'];
  543. $atom_structure['flags']['slide_show'] = (bool) $atom_structure['slide_show_flag'];
  544. $ptv_lookup[0] = 'normal';
  545. $ptv_lookup[1] = 'double';
  546. $ptv_lookup[2] = 'half';
  547. $ptv_lookup[3] = 'full';
  548. $ptv_lookup[4] = 'current';
  549. if (isset($ptv_lookup[$atom_structure['display_size_raw']])) {
  550. $atom_structure['display_size'] = $ptv_lookup[$atom_structure['display_size_raw']];
  551. } else {
  552. $info['warning'][] = 'unknown "ptv " display constant ('.$atom_structure['display_size_raw'].')';
  553. }
  554. break;
  555. case 'stsd': // Sample Table Sample Description atom
  556. $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
  557. $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000
  558. $atom_structure['number_entries'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4));
  559. $stsdEntriesDataOffset = 8;
  560. for ($i = 0; $i < $atom_structure['number_entries']; $i++) {
  561. $atom_structure['sample_description_table'][$i]['size'] = getid3_lib::BigEndian2Int(substr($atom_data, $stsdEntriesDataOffset, 4));
  562. $stsdEntriesDataOffset += 4;
  563. $atom_structure['sample_description_table'][$i]['data_format'] = substr($atom_data, $stsdEntriesDataOffset, 4);
  564. $stsdEntriesDataOffset += 4;
  565. $atom_structure['sample_description_table'][$i]['reserved'] = getid3_lib::BigEndian2Int(substr($atom_data, $stsdEntriesDataOffset, 6));
  566. $stsdEntriesDataOffset += 6;
  567. $atom_structure['sample_description_table'][$i]['reference_index'] = getid3_lib::BigEndian2Int(substr($atom_data, $stsdEntriesDataOffset, 2));
  568. $stsdEntriesDataOffset += 2;
  569. $atom_structure['sample_description_table'][$i]['data'] = substr($atom_data, $stsdEntriesDataOffset, ($atom_structure['sample_description_table'][$i]['size'] - 4 - 4 - 6 - 2));
  570. $stsdEntriesDataOffset += ($atom_structure['sample_description_table'][$i]['size'] - 4 - 4 - 6 - 2);
  571. $atom_structure['sample_description_table'][$i]['encoder_version'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 0, 2));
  572. $atom_structure['sample_description_table'][$i]['encoder_revision'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 2, 2));
  573. $atom_structure['sample_description_table'][$i]['encoder_vendor'] = substr($atom_structure['sample_description_table'][$i]['data'], 4, 4);
  574. switch ($atom_structure['sample_description_table'][$i]['encoder_vendor']) {
  575. case "\x00\x00\x00\x00":
  576. // audio tracks
  577. $atom_structure['sample_description_table'][$i]['audio_channels'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 8, 2));
  578. $atom_structure['sample_description_table'][$i]['audio_bit_depth'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 10, 2));
  579. $atom_structure['sample_description_table'][$i]['audio_compression_id'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 12, 2));
  580. $atom_structure['sample_description_table'][$i]['audio_packet_size'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 14, 2));
  581. $atom_structure['sample_description_table'][$i]['audio_sample_rate'] = getid3_lib::FixedPoint16_16(substr($atom_structure['sample_description_table'][$i]['data'], 16, 4));
  582. // video tracks
  583. // http://developer.apple.com/library/mac/#documentation/QuickTime/QTFF/QTFFChap3/qtff3.html
  584. $atom_structure['sample_description_table'][$i]['temporal_quality'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 8, 4));
  585. $atom_structure['sample_description_table'][$i]['spatial_quality'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 12, 4));
  586. $atom_structure['sample_description_table'][$i]['width'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 16, 2));
  587. $atom_structure['sample_description_table'][$i]['height'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 18, 2));
  588. $atom_structure['sample_description_table'][$i]['resolution_x'] = getid3_lib::FixedPoint16_16(substr($atom_structure['sample_description_table'][$i]['data'], 24, 4));
  589. $atom_structure['sample_description_table'][$i]['resolution_y'] = getid3_lib::FixedPoint16_16(substr($atom_structure['sample_description_table'][$i]['data'], 28, 4));
  590. $atom_structure['sample_description_table'][$i]['data_size'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 32, 4));
  591. $atom_structure['sample_description_table'][$i]['frame_count'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 36, 2));
  592. $atom_structure['sample_description_table'][$i]['compressor_name'] = substr($atom_structure['sample_description_table'][$i]['data'], 38, 4);
  593. $atom_structure['sample_description_table'][$i]['pixel_depth'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 42, 2));
  594. $atom_structure['sample_description_table'][$i]['color_table_id'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 44, 2));
  595. switch ($atom_structure['sample_description_table'][$i]['data_format']) {
  596. case '2vuY':
  597. case 'avc1':
  598. case 'cvid':
  599. case 'dvc ':
  600. case 'dvcp':
  601. case 'gif ':
  602. case 'h263':
  603. case 'jpeg':
  604. case 'kpcd':
  605. case 'mjpa':
  606. case 'mjpb':
  607. case 'mp4v':
  608. case 'png ':
  609. case 'raw ':
  610. case 'rle ':
  611. case 'rpza':
  612. case 'smc ':
  613. case 'SVQ1':
  614. case 'SVQ3':
  615. case 'tiff':
  616. case 'v210':
  617. case 'v216':
  618. case 'v308':
  619. case 'v408':
  620. case 'v410':
  621. case 'yuv2':
  622. $info['fileformat'] = 'mp4';
  623. $info['video']['fourcc'] = $atom_structure['sample_description_table'][$i]['data_format'];
  624. // http://www.getid3.org/phpBB3/viewtopic.php?t=1550
  625. //if ((!empty($atom_structure['sample_description_table'][$i]['width']) && !empty($atom_structure['sample_description_table'][$i]['width'])) && (empty($info['video']['resolution_x']) || empty($info['video']['resolution_y']) || (number_format($info['video']['resolution_x'], 6) != number_format(round($info['video']['resolution_x']), 6)) || (number_format($info['video']['resolution_y'], 6) != number_format(round($info['video']['resolution_y']), 6)))) { // ugly check for floating point numbers
  626. if (!empty($atom_structure['sample_description_table'][$i]['width']) && !empty($atom_structure['sample_description_table'][$i]['height'])) {
  627. // assume that values stored here are more important than values stored in [tkhd] atom
  628. $info['video']['resolution_x'] = $atom_structure['sample_description_table'][$i]['width'];
  629. $info['video']['resolution_y'] = $atom_structure['sample_description_table'][$i]['height'];
  630. $info['quicktime']['video']['resolution_x'] = $info['video']['resolution_x'];
  631. $info['quicktime']['video']['resolution_y'] = $info['video']['resolution_y'];
  632. }
  633. break;
  634. case 'qtvr':
  635. $info['video']['dataformat'] = 'quicktimevr';
  636. break;
  637. case 'mp4a':
  638. default:
  639. $info['quicktime']['audio']['codec'] = $this->QuicktimeAudioCodecLookup($atom_structure['sample_description_table'][$i]['data_format']);
  640. $info['quicktime']['audio']['sample_rate'] = $atom_structure['sample_description_table'][$i]['audio_sample_rate'];
  641. $info['quicktime']['audio']['channels'] = $atom_structure['sample_description_table'][$i]['audio_channels'];
  642. $info['quicktime']['audio']['bit_depth'] = $atom_structure['sample_description_table'][$i]['audio_bit_depth'];
  643. $info['audio']['codec'] = $info['quicktime']['audio']['codec'];
  644. $info['audio']['sample_rate'] = $info['quicktime']['audio']['sample_rate'];
  645. $info['audio']['channels'] = $info['quicktime']['audio']['channels'];
  646. $info['audio']['bits_per_sample'] = $info['quicktime']['audio']['bit_depth'];
  647. switch ($atom_structure['sample_description_table'][$i]['data_format']) {
  648. case 'raw ': // PCM
  649. case 'alac': // Apple Lossless Audio Codec
  650. $info['audio']['lossless'] = true;
  651. break;
  652. default:
  653. $info['audio']['lossless'] = false;
  654. break;
  655. }
  656. break;
  657. }
  658. break;
  659. default:
  660. switch ($atom_structure['sample_description_table'][$i]['data_format']) {
  661. case 'mp4s':
  662. $info['fileformat'] = 'mp4';
  663. break;
  664. default:
  665. // video atom
  666. $atom_structure['sample_description_table'][$i]['video_temporal_quality'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 8, 4));
  667. $atom_structure['sample_description_table'][$i]['video_spatial_quality'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 12, 4));
  668. $atom_structure['sample_description_table'][$i]['video_frame_width'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 16, 2));
  669. $atom_structure['sample_description_table'][$i]['video_frame_height'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 18, 2));
  670. $atom_structure['sample_description_table'][$i]['video_resolution_x'] = getid3_lib::FixedPoint16_16(substr($atom_structure['sample_description_table'][$i]['data'], 20, 4));
  671. $atom_structure['sample_description_table'][$i]['video_resolution_y'] = getid3_lib::FixedPoint16_16(substr($atom_structure['sample_description_table'][$i]['data'], 24, 4));
  672. $atom_structure['sample_description_table'][$i]['video_data_size'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 28, 4));
  673. $atom_structure['sample_description_table'][$i]['video_frame_count'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 32, 2));
  674. $atom_structure['sample_description_table'][$i]['video_encoder_name_len'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 34, 1));
  675. $atom_structure['sample_description_table'][$i]['video_encoder_name'] = substr($atom_structure['sample_description_table'][$i]['data'], 35, $atom_structure['sample_description_table'][$i]['video_encoder_name_len']);
  676. $atom_structure['sample_description_table'][$i]['video_pixel_color_depth'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 66, 2));
  677. $atom_structure['sample_description_table'][$i]['video_color_table_id'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 68, 2));
  678. $atom_structure['sample_description_table'][$i]['video_pixel_color_type'] = (($atom_structure['sample_description_table'][$i]['video_pixel_color_depth'] > 32) ? 'grayscale' : 'color');
  679. $atom_structure['sample_description_table'][$i]['video_pixel_color_name'] = $this->QuicktimeColorNameLookup($atom_structure['sample_description_table'][$i]['video_pixel_color_depth']);
  680. if ($atom_structure['sample_description_table'][$i]['video_pixel_color_name'] != 'invalid') {
  681. $info['quicktime']['video']['codec_fourcc'] = $atom_structure['sample_description_table'][$i]['data_format'];
  682. $info['quicktime']['video']['codec_fourcc_lookup'] = $this->QuicktimeVideoCodecLookup($atom_structure['sample_description_table'][$i]['data_format']);
  683. $info['quicktime']['video']['codec'] = (($atom_structure['sample_description_table'][$i]['video_encoder_name_len'] > 0) ? $atom_structure['sample_description_table'][$i]['video_encoder_name'] : $atom_structure['sample_description_table'][$i]['data_format']);
  684. $info['quicktime']['video']['color_depth'] = $atom_structure['sample_description_table'][$i]['video_pixel_color_depth'];
  685. $info['quicktime']['video']['color_depth_name'] = $atom_structure['sample_description_table'][$i]['video_pixel_color_name'];
  686. $info['video']['codec'] = $info['quicktime']['video']['codec'];
  687. $info['video']['bits_per_sample'] = $info['quicktime']['video']['color_depth'];
  688. }
  689. $info['video']['lossless'] = false;
  690. $info['video']['pixel_aspect_ratio'] = (float) 1;
  691. break;
  692. }
  693. break;
  694. }
  695. switch (strtolower($atom_structure['sample_description_table'][$i]['data_format'])) {
  696. case 'mp4a':
  697. $info['audio']['dataformat'] = 'mp4';
  698. $info['quicktime']['audio']['codec'] = 'mp4';
  699. break;
  700. case '3ivx':
  701. case '3iv1':
  702. case '3iv2':
  703. $info['video']['dataformat'] = '3ivx';
  704. break;
  705. case 'xvid':
  706. $info['video']['dataformat'] = 'xvid';
  707. break;
  708. case 'mp4v':
  709. $info['video']['dataformat'] = 'mpeg4';
  710. break;
  711. case 'divx':
  712. case 'div1':
  713. case 'div2':
  714. case 'div3':
  715. case 'div4':
  716. case 'div5':
  717. case 'div6':
  718. $info['video']['dataformat'] = 'divx';
  719. break;
  720. default:
  721. // do nothing
  722. break;
  723. }
  724. unset($atom_structure['sample_description_table'][$i]['data']);
  725. }
  726. break;
  727. case 'stts': // Sample Table Time-to-Sample atom
  728. $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
  729. $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000
  730. $atom_structure['number_entries'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4));
  731. $sttsEntriesDataOffset = 8;
  732. //$FrameRateCalculatorArray = array();
  733. $frames_count = 0;
  734. for ($i = 0; $i < $atom_structure['number_entries']; $i++) {
  735. $atom_structure['time_to_sample_table'][$i]['sample_count'] = getid3_lib::BigEndian2Int(substr($atom_data, $sttsEntriesDataOffset, 4));
  736. $sttsEntriesDataOffset += 4;
  737. $atom_structure['time_to_sample_table'][$i]['sample_duration'] = getid3_lib::BigEndian2Int(substr($atom_data, $sttsEntriesDataOffset, 4));
  738. $sttsEntriesDataOffset += 4;
  739. $frames_count += $atom_structure['time_to_sample_table'][$i]['sample_count'];
  740. // THIS SECTION REPLACED WITH CODE IN "stbl" ATOM
  741. //if (!empty($info['quicktime']['time_scale']) && ($atom_structure['time_to_sample_table'][$i]['sample_duration'] > 0)) {
  742. // $stts_new_framerate = $info['quicktime']['time_scale'] / $atom_structure['time_to_sample_table'][$i]['sample_duration'];
  743. // if ($stts_new_framerate <= 60) {
  744. // // some atoms have durations of "1" giving a very large framerate, which probably is not right
  745. // $info['video']['frame_rate'] = max($info['video']['frame_rate'], $stts_new_framerate);
  746. // }
  747. //}
  748. //
  749. //$FrameRateCalculatorArray[($info['quicktime']['time_scale'] / $atom_structure['time_to_sample_table'][$i]['sample_duration'])] += $atom_structure['time_to_sample_table'][$i]['sample_count'];
  750. }
  751. $info['quicktime']['stts_framecount'][] = $frames_count;
  752. //$sttsFramesTotal = 0;
  753. //$sttsSecondsTotal = 0;
  754. //foreach ($FrameRateCalculatorArray as $frames_per_second => $frame_count) {
  755. // if (($frames_per_second > 60) || ($frames_per_second < 1)) {
  756. // // not video FPS information, probably audio information
  757. // $sttsFramesTotal = 0;
  758. // $sttsSecondsTotal = 0;
  759. // break;
  760. // }
  761. // $sttsFramesTotal += $frame_count;
  762. // $sttsSecondsTotal += $frame_count / $frames_per_second;
  763. //}
  764. //if (($sttsFramesTotal > 0) && ($sttsSecondsTotal > 0)) {
  765. // if (($sttsFramesTotal / $sttsSecondsTotal) > $info['video']['frame_rate']) {
  766. // $info['video']['frame_rate'] = $sttsFramesTotal / $sttsSecondsTotal;
  767. // }
  768. //}
  769. break;
  770. case 'stss': // Sample Table Sync Sample (key frames) atom
  771. if ($ParseAllPossibleAtoms) {
  772. $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
  773. $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000
  774. $atom_structure['number_entries'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4));
  775. $stssEntriesDataOffset = 8;
  776. for ($i = 0; $i < $atom_structure['number_entries']; $i++) {
  777. $atom_structure['time_to_sample_table'][$i] = getid3_lib::BigEndian2Int(substr($atom_data, $stssEntriesDataOffset, 4));
  778. $stssEntriesDataOffset += 4;
  779. }
  780. }
  781. break;
  782. case 'stsc': // Sample Table Sample-to-Chunk atom
  783. if ($ParseAllPossibleAtoms) {
  784. $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
  785. $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000
  786. $atom_structure['number_entries'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4));
  787. $stscEntriesDataOffset = 8;
  788. for ($i = 0; $i < $atom_structure['number_entries']; $i++) {
  789. $atom_structure['sample_to_chunk_table'][$i]['first_chunk'] = getid3_lib::BigEndian2Int(substr($atom_data, $stscEntriesDataOffset, 4));
  790. $stscEntriesDataOffset += 4;
  791. $atom_structure['sample_to_chunk_table'][$i]['samples_per_chunk'] = getid3_lib::BigEndian2Int(substr($atom_data, $stscEntriesDataOffset, 4));
  792. $stscEntriesDataOffset += 4;
  793. $atom_structure['sample_to_chunk_table'][$i]['sample_description'] = getid3_lib::BigEndian2Int(substr($atom_data, $stscEntriesDataOffset, 4));
  794. $stscEntriesDataOffset += 4;
  795. }
  796. }
  797. break;
  798. case 'stsz': // Sample Table SiZe atom
  799. if ($ParseAllPossibleAtoms) {
  800. $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
  801. $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000
  802. $atom_structure['sample_size'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4));
  803. $atom_structure['number_entries'] = getid3_lib::BigEndian2Int(substr($atom_data, 8, 4));
  804. $stszEntriesDataOffset = 12;
  805. if ($atom_structure['sample_size'] == 0) {
  806. for ($i = 0; $i < $atom_structure['number_entries']; $i++) {
  807. $atom_structure['sample_size_table'][$i] = getid3_lib::BigEndian2Int(substr($atom_data, $stszEntriesDataOffset, 4));
  808. $stszEntriesDataOffset += 4;
  809. }
  810. }
  811. }
  812. break;
  813. case 'stco': // Sample Table Chunk Offset atom
  814. if ($ParseAllPossibleAtoms) {
  815. $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
  816. $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000
  817. $atom_structure['number_entries'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4));
  818. $stcoEntriesDataOffset = 8;
  819. for ($i = 0; $i < $atom_structure['number_entries']; $i++) {
  820. $atom_structure['chunk_offset_table'][$i] = getid3_lib::BigEndian2Int(substr($atom_data, $stcoEntriesDataOffset, 4));
  821. $stcoEntriesDataOffset += 4;
  822. }
  823. }
  824. break;
  825. case 'co64': // Chunk Offset 64-bit (version of "stco" that supports > 2GB files)
  826. if ($ParseAllPossibleAtoms) {
  827. $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
  828. $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000
  829. $atom_structure['number_entries'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4));
  830. $stcoEntriesDataOffset = 8;
  831. for ($i = 0; $i < $atom_structure['number_entries']; $i++) {
  832. $atom_structure['chunk_offset_table'][$i] = getid3_lib::BigEndian2Int(substr($atom_data, $stcoEntriesDataOffset, 8));
  833. $stcoEntriesDataOffset += 8;
  834. }
  835. }
  836. break;
  837. case 'dref': // Data REFerence atom
  838. $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
  839. $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000
  840. $atom_structure['number_entries'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4));
  841. $drefDataOffset = 8;
  842. for ($i = 0; $i < $atom_structure['number_entries']; $i++) {
  843. $atom_structure['data_references'][$i]['size'] = getid3_lib::BigEndian2Int(substr($atom_data, $drefDataOffset, 4));
  844. $drefDataOffset += 4;
  845. $atom_structure['data_references'][$i]['type'] = substr($atom_data, $drefDataOffset, 4);
  846. $drefDataOffset += 4;
  847. $atom_structure['data_references'][$i]['version'] = getid3_lib::BigEndian2Int(substr($atom_data, $drefDataOffset, 1));
  848. $drefDataOffset += 1;
  849. $atom_structure['data_references'][$i]['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, $drefDataOffset, 3)); // hardcoded: 0x0000
  850. $drefDataOffset += 3;
  851. $atom_structure['data_references'][$i]['data'] = substr($atom_data, $drefDataOffset, ($atom_structure['data_references'][$i]['size'] - 4 - 4 - 1 - 3));
  852. $drefDataOffset += ($atom_structure['data_references'][$i]['size'] - 4 - 4 - 1 - 3);
  853. $atom_structure['data_references'][$i]['flags']['self_reference'] = (bool) ($atom_structure['data_references'][$i]['flags_raw'] & 0x001);
  854. }
  855. break;
  856. case 'gmin': // base Media INformation atom
  857. $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
  858. $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000
  859. $atom_structure['graphics_mode'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 2));
  860. $atom_structure['opcolor_red'] = getid3_lib::BigEndian2Int(substr($atom_data, 6, 2));
  861. $atom_structure['opcolor_green'] = getid3_lib::BigEndian2Int(substr($atom_data, 8, 2));
  862. $atom_structure['opcolor_blue'] = getid3_lib::BigEndian2Int(substr($atom_data, 10, 2));
  863. $atom_structure['balance'] = getid3_lib::BigEndian2Int(substr($atom_data, 12, 2));
  864. $atom_structure['reserved'] = getid3_lib::BigEndian2Int(substr($atom_data, 14, 2));
  865. break;
  866. case 'smhd': // Sound Media information HeaDer atom
  867. $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
  868. $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000
  869. $atom_structure['balance'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 2));
  870. $atom_structure['reserved'] = getid3_lib::BigEndian2Int(substr($atom_data, 6, 2));
  871. break;
  872. case 'vmhd': // Video Media information HeaDer atom
  873. $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
  874. $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3));
  875. $atom_structure['graphics_mode'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 2));