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