module.audio-video.real.php 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546
  1. <?php
  2. /////////////////////////////////////////////////////////////////
  3. /// getID3() by James Heinrich <info@getid3.org> //
  4. // available at https://github.com/JamesHeinrich/getID3 //
  5. // or https://www.getid3.org //
  6. // or http://getid3.sourceforge.net //
  7. // see readme.txt for more details //
  8. /////////////////////////////////////////////////////////////////
  9. // //
  10. // module.audio-video.real.php //
  11. // module for analyzing Real Audio/Video files //
  12. // dependencies: module.audio-video.riff.php //
  13. // ///
  14. /////////////////////////////////////////////////////////////////
  15. if (!defined('GETID3_INCLUDEPATH')) { // prevent path-exposing attacks that access modules directly on public webservers
  16. exit;
  17. }
  18. getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio-video.riff.php', __FILE__, true);
  19. class getid3_real extends getid3_handler
  20. {
  21. /**
  22. * @return bool
  23. */
  24. public function Analyze() {
  25. $info = &$this->getid3->info;
  26. $info['fileformat'] = 'real';
  27. $info['bitrate'] = 0;
  28. $info['playtime_seconds'] = 0;
  29. $this->fseek($info['avdataoffset']);
  30. $ChunkCounter = 0;
  31. while ($this->ftell() < $info['avdataend']) {
  32. $ChunkData = $this->fread(8);
  33. $ChunkName = substr($ChunkData, 0, 4);
  34. $ChunkSize = getid3_lib::BigEndian2Int(substr($ChunkData, 4, 4));
  35. if ($ChunkName == '.ra'."\xFD") {
  36. $ChunkData .= $this->fread($ChunkSize - 8);
  37. if ($this->ParseOldRAheader(substr($ChunkData, 0, 128), $info['real']['old_ra_header'])) {
  38. $info['audio']['dataformat'] = 'real';
  39. $info['audio']['lossless'] = false;
  40. $info['audio']['sample_rate'] = $info['real']['old_ra_header']['sample_rate'];
  41. $info['audio']['bits_per_sample'] = $info['real']['old_ra_header']['bits_per_sample'];
  42. $info['audio']['channels'] = $info['real']['old_ra_header']['channels'];
  43. $info['playtime_seconds'] = 60 * ($info['real']['old_ra_header']['audio_bytes'] / $info['real']['old_ra_header']['bytes_per_minute']);
  44. $info['audio']['bitrate'] = 8 * ($info['real']['old_ra_header']['audio_bytes'] / $info['playtime_seconds']);
  45. $info['audio']['codec'] = $this->RealAudioCodecFourCClookup($info['real']['old_ra_header']['fourcc'], $info['audio']['bitrate']);
  46. foreach ($info['real']['old_ra_header']['comments'] as $key => $valuearray) {
  47. if (strlen(trim($valuearray[0])) > 0) {
  48. $info['real']['comments'][$key][] = trim($valuearray[0]);
  49. }
  50. }
  51. return true;
  52. }
  53. $this->error('There was a problem parsing this RealAudio file. Please submit it for analysis to info@getid3.org');
  54. unset($info['bitrate']);
  55. unset($info['playtime_seconds']);
  56. return false;
  57. }
  58. // shortcut
  59. $info['real']['chunks'][$ChunkCounter] = array();
  60. $thisfile_real_chunks_currentchunk = &$info['real']['chunks'][$ChunkCounter];
  61. $thisfile_real_chunks_currentchunk['name'] = $ChunkName;
  62. $thisfile_real_chunks_currentchunk['offset'] = $this->ftell() - 8;
  63. $thisfile_real_chunks_currentchunk['length'] = $ChunkSize;
  64. if (($thisfile_real_chunks_currentchunk['offset'] + $thisfile_real_chunks_currentchunk['length']) > $info['avdataend']) {
  65. $this->warning('Chunk "'.$thisfile_real_chunks_currentchunk['name'].'" at offset '.$thisfile_real_chunks_currentchunk['offset'].' claims to be '.$thisfile_real_chunks_currentchunk['length'].' bytes long, which is beyond end of file');
  66. return false;
  67. }
  68. if ($ChunkSize > ($this->getid3->fread_buffer_size() + 8)) {
  69. $ChunkData .= $this->fread($this->getid3->fread_buffer_size() - 8);
  70. $this->fseek($thisfile_real_chunks_currentchunk['offset'] + $ChunkSize);
  71. } elseif(($ChunkSize - 8) > 0) {
  72. $ChunkData .= $this->fread($ChunkSize - 8);
  73. }
  74. $offset = 8;
  75. switch ($ChunkName) {
  76. case '.RMF': // RealMedia File Header
  77. $thisfile_real_chunks_currentchunk['object_version'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 2));
  78. $offset += 2;
  79. switch ($thisfile_real_chunks_currentchunk['object_version']) {
  80. case 0:
  81. $thisfile_real_chunks_currentchunk['file_version'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4));
  82. $offset += 4;
  83. $thisfile_real_chunks_currentchunk['headers_count'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4));
  84. $offset += 4;
  85. break;
  86. default:
  87. //$this->warning('Expected .RMF-object_version to be "0", actual value is "'.$thisfile_real_chunks_currentchunk['object_version'].'" (should not be a problem)');
  88. break;
  89. }
  90. break;
  91. case 'PROP': // Properties Header
  92. $thisfile_real_chunks_currentchunk['object_version'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 2));
  93. $offset += 2;
  94. if ($thisfile_real_chunks_currentchunk['object_version'] == 0) {
  95. $thisfile_real_chunks_currentchunk['max_bit_rate'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4));
  96. $offset += 4;
  97. $thisfile_real_chunks_currentchunk['avg_bit_rate'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4));
  98. $offset += 4;
  99. $thisfile_real_chunks_currentchunk['max_packet_size'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4));
  100. $offset += 4;
  101. $thisfile_real_chunks_currentchunk['avg_packet_size'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4));
  102. $offset += 4;
  103. $thisfile_real_chunks_currentchunk['num_packets'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4));
  104. $offset += 4;
  105. $thisfile_real_chunks_currentchunk['duration'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4));
  106. $offset += 4;
  107. $thisfile_real_chunks_currentchunk['preroll'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4));
  108. $offset += 4;
  109. $thisfile_real_chunks_currentchunk['index_offset'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4));
  110. $offset += 4;
  111. $thisfile_real_chunks_currentchunk['data_offset'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4));
  112. $offset += 4;
  113. $thisfile_real_chunks_currentchunk['num_streams'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 2));
  114. $offset += 2;
  115. $thisfile_real_chunks_currentchunk['flags_raw'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 2));
  116. $offset += 2;
  117. $info['playtime_seconds'] = $thisfile_real_chunks_currentchunk['duration'] / 1000;
  118. if ($thisfile_real_chunks_currentchunk['duration'] > 0) {
  119. $info['bitrate'] += $thisfile_real_chunks_currentchunk['avg_bit_rate'];
  120. }
  121. $thisfile_real_chunks_currentchunk['flags']['save_enabled'] = (bool) ($thisfile_real_chunks_currentchunk['flags_raw'] & 0x0001);
  122. $thisfile_real_chunks_currentchunk['flags']['perfect_play'] = (bool) ($thisfile_real_chunks_currentchunk['flags_raw'] & 0x0002);
  123. $thisfile_real_chunks_currentchunk['flags']['live_broadcast'] = (bool) ($thisfile_real_chunks_currentchunk['flags_raw'] & 0x0004);
  124. }
  125. break;
  126. case 'MDPR': // Media Properties Header
  127. $thisfile_real_chunks_currentchunk['object_version'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 2));
  128. $offset += 2;
  129. if ($thisfile_real_chunks_currentchunk['object_version'] == 0) {
  130. $thisfile_real_chunks_currentchunk['stream_number'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 2));
  131. $offset += 2;
  132. $thisfile_real_chunks_currentchunk['max_bit_rate'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4));
  133. $offset += 4;
  134. $thisfile_real_chunks_currentchunk['avg_bit_rate'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4));
  135. $offset += 4;
  136. $thisfile_real_chunks_currentchunk['max_packet_size'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4));
  137. $offset += 4;
  138. $thisfile_real_chunks_currentchunk['avg_packet_size'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4));
  139. $offset += 4;
  140. $thisfile_real_chunks_currentchunk['start_time'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4));
  141. $offset += 4;
  142. $thisfile_real_chunks_currentchunk['preroll'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4));
  143. $offset += 4;
  144. $thisfile_real_chunks_currentchunk['duration'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4));
  145. $offset += 4;
  146. $thisfile_real_chunks_currentchunk['stream_name_size'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 1));
  147. $offset += 1;
  148. $thisfile_real_chunks_currentchunk['stream_name'] = substr($ChunkData, $offset, $thisfile_real_chunks_currentchunk['stream_name_size']);
  149. $offset += $thisfile_real_chunks_currentchunk['stream_name_size'];
  150. $thisfile_real_chunks_currentchunk['mime_type_size'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 1));
  151. $offset += 1;
  152. $thisfile_real_chunks_currentchunk['mime_type'] = substr($ChunkData, $offset, $thisfile_real_chunks_currentchunk['mime_type_size']);
  153. $offset += $thisfile_real_chunks_currentchunk['mime_type_size'];
  154. $thisfile_real_chunks_currentchunk['type_specific_len'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4));
  155. $offset += 4;
  156. $thisfile_real_chunks_currentchunk['type_specific_data'] = substr($ChunkData, $offset, $thisfile_real_chunks_currentchunk['type_specific_len']);
  157. $offset += $thisfile_real_chunks_currentchunk['type_specific_len'];
  158. // shortcut
  159. $thisfile_real_chunks_currentchunk_typespecificdata = &$thisfile_real_chunks_currentchunk['type_specific_data'];
  160. switch ($thisfile_real_chunks_currentchunk['mime_type']) {
  161. case 'video/x-pn-realvideo':
  162. case 'video/x-pn-multirate-realvideo':
  163. // http://www.freelists.org/archives/matroska-devel/07-2003/msg00010.html
  164. // shortcut
  165. $thisfile_real_chunks_currentchunk['video_info'] = array();
  166. $thisfile_real_chunks_currentchunk_videoinfo = &$thisfile_real_chunks_currentchunk['video_info'];
  167. $thisfile_real_chunks_currentchunk_videoinfo['dwSize'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, 0, 4));
  168. $thisfile_real_chunks_currentchunk_videoinfo['fourcc1'] = substr($thisfile_real_chunks_currentchunk_typespecificdata, 4, 4);
  169. $thisfile_real_chunks_currentchunk_videoinfo['fourcc2'] = substr($thisfile_real_chunks_currentchunk_typespecificdata, 8, 4);
  170. $thisfile_real_chunks_currentchunk_videoinfo['width'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, 12, 2));
  171. $thisfile_real_chunks_currentchunk_videoinfo['height'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, 14, 2));
  172. $thisfile_real_chunks_currentchunk_videoinfo['bits_per_sample'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, 16, 2));
  173. //$thisfile_real_chunks_currentchunk_videoinfo['unknown1'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, 18, 2));
  174. //$thisfile_real_chunks_currentchunk_videoinfo['unknown2'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, 20, 2));
  175. $thisfile_real_chunks_currentchunk_videoinfo['frames_per_second'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, 22, 2));
  176. //$thisfile_real_chunks_currentchunk_videoinfo['unknown3'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, 24, 2));
  177. //$thisfile_real_chunks_currentchunk_videoinfo['unknown4'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, 26, 2));
  178. //$thisfile_real_chunks_currentchunk_videoinfo['unknown5'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, 28, 2));
  179. //$thisfile_real_chunks_currentchunk_videoinfo['unknown6'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, 30, 2));
  180. //$thisfile_real_chunks_currentchunk_videoinfo['unknown7'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, 32, 2));
  181. //$thisfile_real_chunks_currentchunk_videoinfo['unknown8'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, 34, 2));
  182. //$thisfile_real_chunks_currentchunk_videoinfo['unknown9'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, 36, 2));
  183. $thisfile_real_chunks_currentchunk_videoinfo['codec'] = getid3_riff::fourccLookup($thisfile_real_chunks_currentchunk_videoinfo['fourcc2']);
  184. $info['video']['resolution_x'] = $thisfile_real_chunks_currentchunk_videoinfo['width'];
  185. $info['video']['resolution_y'] = $thisfile_real_chunks_currentchunk_videoinfo['height'];
  186. $info['video']['frame_rate'] = (float) $thisfile_real_chunks_currentchunk_videoinfo['frames_per_second'];
  187. $info['video']['codec'] = $thisfile_real_chunks_currentchunk_videoinfo['codec'];
  188. $info['video']['bits_per_sample'] = $thisfile_real_chunks_currentchunk_videoinfo['bits_per_sample'];
  189. break;
  190. case 'audio/x-pn-realaudio':
  191. case 'audio/x-pn-multirate-realaudio':
  192. $this->ParseOldRAheader($thisfile_real_chunks_currentchunk_typespecificdata, $parsedAudioData);
  193. $thisfile_real_chunks_currentchunk['parsed_audio_data'] = &$parsedAudioData;
  194. $info['audio']['sample_rate'] = $thisfile_real_chunks_currentchunk['parsed_audio_data']['sample_rate'];
  195. $info['audio']['bits_per_sample'] = $thisfile_real_chunks_currentchunk['parsed_audio_data']['bits_per_sample'];
  196. $info['audio']['channels'] = $thisfile_real_chunks_currentchunk['parsed_audio_data']['channels'];
  197. if (!empty($info['audio']['dataformat'])) {
  198. foreach ($info['audio'] as $key => $value) {
  199. if ($key != 'streams') {
  200. $info['audio']['streams'][$thisfile_real_chunks_currentchunk['stream_number']][$key] = $value;
  201. }
  202. }
  203. }
  204. break;
  205. case 'logical-fileinfo':
  206. // shortcut
  207. $thisfile_real_chunks_currentchunk['logical_fileinfo'] = array();
  208. $thisfile_real_chunks_currentchunk_logicalfileinfo = &$thisfile_real_chunks_currentchunk['logical_fileinfo'];
  209. $thisfile_real_chunks_currentchunk_logicalfileinfo_offset = 0;
  210. $thisfile_real_chunks_currentchunk_logicalfileinfo['logical_fileinfo_length'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, $thisfile_real_chunks_currentchunk_logicalfileinfo_offset, 4));
  211. $thisfile_real_chunks_currentchunk_logicalfileinfo_offset += 4;
  212. //$thisfile_real_chunks_currentchunk_logicalfileinfo['unknown1'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, $thisfile_real_chunks_currentchunk_logicalfileinfo_offset, 4));
  213. $thisfile_real_chunks_currentchunk_logicalfileinfo_offset += 4;
  214. $thisfile_real_chunks_currentchunk_logicalfileinfo['num_tags'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, $thisfile_real_chunks_currentchunk_logicalfileinfo_offset, 4));
  215. $thisfile_real_chunks_currentchunk_logicalfileinfo_offset += 4;
  216. //$thisfile_real_chunks_currentchunk_logicalfileinfo['unknown2'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, $thisfile_real_chunks_currentchunk_logicalfileinfo_offset, 4));
  217. $thisfile_real_chunks_currentchunk_logicalfileinfo_offset += 4;
  218. //$thisfile_real_chunks_currentchunk_logicalfileinfo['d'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, $thisfile_real_chunks_currentchunk_logicalfileinfo_offset, 1));
  219. //$thisfile_real_chunks_currentchunk_logicalfileinfo['one_type'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, $thisfile_real_chunks_currentchunk_logicalfileinfo_offset, 4));
  220. //$thisfile_real_chunks_currentchunk_logicalfileinfo_thislength = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, 4 + $thisfile_real_chunks_currentchunk_logicalfileinfo_offset, 2));
  221. //$thisfile_real_chunks_currentchunk_logicalfileinfo['one'] = substr($thisfile_real_chunks_currentchunk_typespecificdata, 6 + $thisfile_real_chunks_currentchunk_logicalfileinfo_offset, $thisfile_real_chunks_currentchunk_logicalfileinfo_thislength);
  222. //$thisfile_real_chunks_currentchunk_logicalfileinfo_offset += (6 + $thisfile_real_chunks_currentchunk_logicalfileinfo_thislength);
  223. break;
  224. }
  225. if (empty($info['playtime_seconds'])) {
  226. $info['playtime_seconds'] = max($info['playtime_seconds'], ($thisfile_real_chunks_currentchunk['duration'] + $thisfile_real_chunks_currentchunk['start_time']) / 1000);
  227. }
  228. if ($thisfile_real_chunks_currentchunk['duration'] > 0) {
  229. switch ($thisfile_real_chunks_currentchunk['mime_type']) {
  230. case 'audio/x-pn-realaudio':
  231. case 'audio/x-pn-multirate-realaudio':
  232. $info['audio']['bitrate'] = (isset($info['audio']['bitrate']) ? $info['audio']['bitrate'] : 0) + $thisfile_real_chunks_currentchunk['avg_bit_rate'];
  233. $info['audio']['codec'] = $this->RealAudioCodecFourCClookup($thisfile_real_chunks_currentchunk['parsed_audio_data']['fourcc'], $info['audio']['bitrate']);
  234. $info['audio']['dataformat'] = 'real';
  235. $info['audio']['lossless'] = false;
  236. break;
  237. case 'video/x-pn-realvideo':
  238. case 'video/x-pn-multirate-realvideo':
  239. $info['video']['bitrate'] = (isset($info['video']['bitrate']) ? $info['video']['bitrate'] : 0) + $thisfile_real_chunks_currentchunk['avg_bit_rate'];
  240. $info['video']['bitrate_mode'] = 'cbr';
  241. $info['video']['dataformat'] = 'real';
  242. $info['video']['lossless'] = false;
  243. $info['video']['pixel_aspect_ratio'] = (float) 1;
  244. break;
  245. case 'audio/x-ralf-mpeg4-generic':
  246. $info['audio']['bitrate'] = (isset($info['audio']['bitrate']) ? $info['audio']['bitrate'] : 0) + $thisfile_real_chunks_currentchunk['avg_bit_rate'];
  247. $info['audio']['codec'] = 'RealAudio Lossless';
  248. $info['audio']['dataformat'] = 'real';
  249. $info['audio']['lossless'] = true;
  250. break;
  251. }
  252. $info['bitrate'] = (isset($info['video']['bitrate']) ? $info['video']['bitrate'] : 0) + (isset($info['audio']['bitrate']) ? $info['audio']['bitrate'] : 0);
  253. }
  254. }
  255. break;
  256. case 'CONT': // Content Description Header (text comments)
  257. $thisfile_real_chunks_currentchunk['object_version'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 2));
  258. $offset += 2;
  259. if ($thisfile_real_chunks_currentchunk['object_version'] == 0) {
  260. $thisfile_real_chunks_currentchunk['title_len'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 2));
  261. $offset += 2;
  262. $thisfile_real_chunks_currentchunk['title'] = (string) substr($ChunkData, $offset, $thisfile_real_chunks_currentchunk['title_len']);
  263. $offset += $thisfile_real_chunks_currentchunk['title_len'];
  264. $thisfile_real_chunks_currentchunk['artist_len'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 2));
  265. $offset += 2;
  266. $thisfile_real_chunks_currentchunk['artist'] = (string) substr($ChunkData, $offset, $thisfile_real_chunks_currentchunk['artist_len']);
  267. $offset += $thisfile_real_chunks_currentchunk['artist_len'];
  268. $thisfile_real_chunks_currentchunk['copyright_len'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 2));
  269. $offset += 2;
  270. $thisfile_real_chunks_currentchunk['copyright'] = (string) substr($ChunkData, $offset, $thisfile_real_chunks_currentchunk['copyright_len']);
  271. $offset += $thisfile_real_chunks_currentchunk['copyright_len'];
  272. $thisfile_real_chunks_currentchunk['comment_len'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 2));
  273. $offset += 2;
  274. $thisfile_real_chunks_currentchunk['comment'] = (string) substr($ChunkData, $offset, $thisfile_real_chunks_currentchunk['comment_len']);
  275. $offset += $thisfile_real_chunks_currentchunk['comment_len'];
  276. $commentkeystocopy = array('title'=>'title', 'artist'=>'artist', 'copyright'=>'copyright', 'comment'=>'comment');
  277. foreach ($commentkeystocopy as $key => $val) {
  278. if ($thisfile_real_chunks_currentchunk[$key]) {
  279. $info['real']['comments'][$val][] = trim($thisfile_real_chunks_currentchunk[$key]);
  280. }
  281. }
  282. }
  283. break;
  284. case 'DATA': // Data Chunk Header
  285. // do nothing
  286. break;
  287. case 'INDX': // Index Section Header
  288. $thisfile_real_chunks_currentchunk['object_version'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 2));
  289. $offset += 2;
  290. if ($thisfile_real_chunks_currentchunk['object_version'] == 0) {
  291. $thisfile_real_chunks_currentchunk['num_indices'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4));
  292. $offset += 4;
  293. $thisfile_real_chunks_currentchunk['stream_number'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 2));
  294. $offset += 2;
  295. $thisfile_real_chunks_currentchunk['next_index_header'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4));
  296. $offset += 4;
  297. if ($thisfile_real_chunks_currentchunk['next_index_header'] == 0) {
  298. // last index chunk found, ignore rest of file
  299. break 2;
  300. } else {
  301. // non-last index chunk, seek to next index chunk (skipping actual index data)
  302. $this->fseek($thisfile_real_chunks_currentchunk['next_index_header']);
  303. }
  304. }
  305. break;
  306. default:
  307. $this->warning('Unhandled RealMedia chunk "'.$ChunkName.'" at offset '.$thisfile_real_chunks_currentchunk['offset']);
  308. break;
  309. }
  310. $ChunkCounter++;
  311. }
  312. if (!empty($info['audio']['streams'])) {
  313. $info['audio']['bitrate'] = 0;
  314. foreach ($info['audio']['streams'] as $key => $valuearray) {
  315. $info['audio']['bitrate'] += $valuearray['bitrate'];
  316. }
  317. }
  318. return true;
  319. }
  320. /**
  321. * @param string $OldRAheaderData
  322. * @param array $ParsedArray
  323. *
  324. * @return bool
  325. */
  326. public function ParseOldRAheader($OldRAheaderData, &$ParsedArray) {
  327. // http://www.freelists.org/archives/matroska-devel/07-2003/msg00010.html
  328. $ParsedArray = array();
  329. $ParsedArray['magic'] = substr($OldRAheaderData, 0, 4);
  330. if ($ParsedArray['magic'] != '.ra'."\xFD") {
  331. return false;
  332. }
  333. $ParsedArray['version1'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 4, 2));
  334. if ($ParsedArray['version1'] < 3) {
  335. return false;
  336. } elseif ($ParsedArray['version1'] == 3) {
  337. $ParsedArray['fourcc1'] = '.ra3';
  338. $ParsedArray['bits_per_sample'] = 16; // hard-coded for old versions?
  339. $ParsedArray['sample_rate'] = 8000; // hard-coded for old versions?
  340. $ParsedArray['header_size'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 6, 2));
  341. $ParsedArray['channels'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 8, 2)); // always 1 (?)
  342. //$ParsedArray['unknown1'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 10, 2));
  343. //$ParsedArray['unknown2'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 12, 2));
  344. //$ParsedArray['unknown3'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 14, 2));
  345. $ParsedArray['bytes_per_minute'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 16, 2));
  346. $ParsedArray['audio_bytes'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 18, 4));
  347. $ParsedArray['comments_raw'] = substr($OldRAheaderData, 22, $ParsedArray['header_size'] - 22 + 1); // not including null terminator
  348. $commentoffset = 0;
  349. $commentlength = getid3_lib::BigEndian2Int(substr($ParsedArray['comments_raw'], $commentoffset++, 1));
  350. $ParsedArray['comments']['title'][] = substr($ParsedArray['comments_raw'], $commentoffset, $commentlength);
  351. $commentoffset += $commentlength;
  352. $commentlength = getid3_lib::BigEndian2Int(substr($ParsedArray['comments_raw'], $commentoffset++, 1));
  353. $ParsedArray['comments']['artist'][] = substr($ParsedArray['comments_raw'], $commentoffset, $commentlength);
  354. $commentoffset += $commentlength;
  355. $commentlength = getid3_lib::BigEndian2Int(substr($ParsedArray['comments_raw'], $commentoffset++, 1));
  356. $ParsedArray['comments']['copyright'][] = substr($ParsedArray['comments_raw'], $commentoffset, $commentlength);
  357. $commentoffset += $commentlength;
  358. $commentoffset++; // final null terminator (?)
  359. $commentoffset++; // fourcc length (?) should be 4
  360. $ParsedArray['fourcc'] = substr($OldRAheaderData, 23 + $commentoffset, 4);
  361. } elseif ($ParsedArray['version1'] <= 5) {
  362. //$ParsedArray['unknown1'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 6, 2));
  363. $ParsedArray['fourcc1'] = substr($OldRAheaderData, 8, 4);
  364. $ParsedArray['file_size'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 12, 4));
  365. $ParsedArray['version2'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 16, 2));
  366. $ParsedArray['header_size'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 18, 4));
  367. $ParsedArray['codec_flavor_id'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 22, 2));
  368. $ParsedArray['coded_frame_size'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 24, 4));
  369. $ParsedArray['audio_bytes'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 28, 4));
  370. $ParsedArray['bytes_per_minute'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 32, 4));
  371. //$ParsedArray['unknown5'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 36, 4));
  372. $ParsedArray['sub_packet_h'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 40, 2));
  373. $ParsedArray['frame_size'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 42, 2));
  374. $ParsedArray['sub_packet_size'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 44, 2));
  375. //$ParsedArray['unknown6'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 46, 2));
  376. switch ($ParsedArray['version1']) {
  377. case 4:
  378. $ParsedArray['sample_rate'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 48, 2));
  379. //$ParsedArray['unknown8'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 50, 2));
  380. $ParsedArray['bits_per_sample'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 52, 2));
  381. $ParsedArray['channels'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 54, 2));
  382. $ParsedArray['length_fourcc2'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 56, 1));
  383. $ParsedArray['fourcc2'] = substr($OldRAheaderData, 57, 4);
  384. $ParsedArray['length_fourcc3'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 61, 1));
  385. $ParsedArray['fourcc3'] = substr($OldRAheaderData, 62, 4);
  386. //$ParsedArray['unknown9'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 66, 1));
  387. //$ParsedArray['unknown10'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 67, 2));
  388. $ParsedArray['comments_raw'] = substr($OldRAheaderData, 69, $ParsedArray['header_size'] - 69 + 16);
  389. $commentoffset = 0;
  390. $commentlength = getid3_lib::BigEndian2Int(substr($ParsedArray['comments_raw'], $commentoffset++, 1));
  391. $ParsedArray['comments']['title'][] = substr($ParsedArray['comments_raw'], $commentoffset, $commentlength);
  392. $commentoffset += $commentlength;
  393. $commentlength = getid3_lib::BigEndian2Int(substr($ParsedArray['comments_raw'], $commentoffset++, 1));
  394. $ParsedArray['comments']['artist'][] = substr($ParsedArray['comments_raw'], $commentoffset, $commentlength);
  395. $commentoffset += $commentlength;
  396. $commentlength = getid3_lib::BigEndian2Int(substr($ParsedArray['comments_raw'], $commentoffset++, 1));
  397. $ParsedArray['comments']['copyright'][] = substr($ParsedArray['comments_raw'], $commentoffset, $commentlength);
  398. $commentoffset += $commentlength;
  399. break;
  400. case 5:
  401. $ParsedArray['sample_rate'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 48, 4));
  402. $ParsedArray['sample_rate2'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 52, 4));
  403. $ParsedArray['bits_per_sample'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 56, 4));
  404. $ParsedArray['channels'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 60, 2));
  405. $ParsedArray['genr'] = substr($OldRAheaderData, 62, 4);
  406. $ParsedArray['fourcc3'] = substr($OldRAheaderData, 66, 4);
  407. $ParsedArray['comments'] = array();
  408. break;
  409. }
  410. $ParsedArray['fourcc'] = $ParsedArray['fourcc3'];
  411. }
  412. /** @var string[]|false[] $value */
  413. foreach ($ParsedArray['comments'] as $key => $value) {
  414. if ($value[0] === false) {
  415. $ParsedArray['comments'][$key][0] = '';
  416. }
  417. }
  418. return true;
  419. }
  420. /**
  421. * @param string $fourcc
  422. * @param int $bitrate
  423. *
  424. * @return string
  425. */
  426. public function RealAudioCodecFourCClookup($fourcc, $bitrate) {
  427. static $RealAudioCodecFourCClookup = array();
  428. if (empty($RealAudioCodecFourCClookup)) {
  429. // http://www.its.msstate.edu/net/real/reports/config/tags.stats
  430. // http://www.freelists.org/archives/matroska-devel/06-2003/fullthread18.html
  431. $RealAudioCodecFourCClookup['14_4'][8000] = 'RealAudio v2 (14.4kbps)';
  432. $RealAudioCodecFourCClookup['14.4'][8000] = 'RealAudio v2 (14.4kbps)';
  433. $RealAudioCodecFourCClookup['lpcJ'][8000] = 'RealAudio v2 (14.4kbps)';
  434. $RealAudioCodecFourCClookup['28_8'][15200] = 'RealAudio v2 (28.8kbps)';
  435. $RealAudioCodecFourCClookup['28.8'][15200] = 'RealAudio v2 (28.8kbps)';
  436. $RealAudioCodecFourCClookup['sipr'][4933] = 'RealAudio v4 (5kbps Voice)';
  437. $RealAudioCodecFourCClookup['sipr'][6444] = 'RealAudio v4 (6.5kbps Voice)';
  438. $RealAudioCodecFourCClookup['sipr'][8444] = 'RealAudio v4 (8.5kbps Voice)';
  439. $RealAudioCodecFourCClookup['sipr'][16000] = 'RealAudio v4 (16kbps Wideband)';
  440. $RealAudioCodecFourCClookup['dnet'][8000] = 'RealAudio v3 (8kbps Music)';
  441. $RealAudioCodecFourCClookup['dnet'][16000] = 'RealAudio v3 (16kbps Music Low Response)';
  442. $RealAudioCodecFourCClookup['dnet'][15963] = 'RealAudio v3 (16kbps Music Mid/High Response)';
  443. $RealAudioCodecFourCClookup['dnet'][20000] = 'RealAudio v3 (20kbps Music Stereo)';
  444. $RealAudioCodecFourCClookup['dnet'][32000] = 'RealAudio v3 (32kbps Music Mono)';
  445. $RealAudioCodecFourCClookup['dnet'][31951] = 'RealAudio v3 (32kbps Music Stereo)';
  446. $RealAudioCodecFourCClookup['dnet'][39965] = 'RealAudio v3 (40kbps Music Mono)';
  447. $RealAudioCodecFourCClookup['dnet'][40000] = 'RealAudio v3 (40kbps Music Stereo)';
  448. $RealAudioCodecFourCClookup['dnet'][79947] = 'RealAudio v3 (80kbps Music Mono)';
  449. $RealAudioCodecFourCClookup['dnet'][80000] = 'RealAudio v3 (80kbps Music Stereo)';
  450. $RealAudioCodecFourCClookup['dnet'][0] = 'RealAudio v3';
  451. $RealAudioCodecFourCClookup['sipr'][0] = 'RealAudio v4';
  452. $RealAudioCodecFourCClookup['cook'][0] = 'RealAudio G2';
  453. $RealAudioCodecFourCClookup['atrc'][0] = 'RealAudio 8';
  454. }
  455. $roundbitrate = intval(round($bitrate));
  456. if (isset($RealAudioCodecFourCClookup[$fourcc][$roundbitrate])) {
  457. return $RealAudioCodecFourCClookup[$fourcc][$roundbitrate];
  458. } elseif (isset($RealAudioCodecFourCClookup[$fourcc][0])) {
  459. return $RealAudioCodecFourCClookup[$fourcc][0];
  460. }
  461. return $fourcc;
  462. }
  463. }