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