Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
0.00% |
0 / 241 |
|
0.00% |
0 / 10 |
CRAP | |
0.00% |
0 / 1 |
File_Ogg | |
0.00% |
0 / 225 |
|
0.00% |
0 / 10 |
4556 | |
0.00% |
0 / 1 |
__construct | |
0.00% |
0 / 12 |
|
0.00% |
0 / 1 |
20 | |||
_littleEndianBin2Hex | |
0.00% |
0 / 7 |
|
0.00% |
0 / 1 |
6 | |||
_readBigEndian | |
0.00% |
0 / 36 |
|
0.00% |
0 / 1 |
110 | |||
_readLittleEndian | |
0.00% |
0 / 34 |
|
0.00% |
0 / 1 |
110 | |||
_decodePageHeader | |
0.00% |
0 / 53 |
|
0.00% |
0 / 1 |
110 | |||
_splitStreams | |
0.00% |
0 / 52 |
|
0.00% |
0 / 1 |
306 | |||
getStream | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
6 | |||
listStreams | |
0.00% |
0 / 24 |
|
0.00% |
0 / 1 |
90 | |||
getStartOffset | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
6 | |||
getLength | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 |
1 | <?php |
2 | /* vim: set expandtab tabstop=4 shiftwidth=4: */ |
3 | // +----------------------------------------------------------------------------+ |
4 | // | File_Ogg PEAR Package for Accessing Ogg Bitstreams | |
5 | // | Copyright (c) 2005-2007 | |
6 | // | David Grant <david@grant.org.uk> | |
7 | // | Tim Starling <tstarling@wikimedia.org> | |
8 | // +----------------------------------------------------------------------------+ |
9 | // | This library is free software; you can redistribute it and/or | |
10 | // | modify it under the terms of the GNU Lesser General Public | |
11 | // | License as published by the Free Software Foundation; either | |
12 | // | version 2.1 of the License, or (at your option) any later version. | |
13 | // | | |
14 | // | This library is distributed in the hope that it will be useful, | |
15 | // | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
16 | // | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
17 | // | Lesser General Public License for more details. | |
18 | // | | |
19 | // | You should have received a copy of the GNU Lesser General Public | |
20 | // | License along with this library; if not, write to the Free Software | |
21 | // | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | |
22 | // +----------------------------------------------------------------------------+ |
23 | |
24 | /** |
25 | * @author David Grant <david@grant.org.uk>, Tim Starling <tstarling@wikimedia.org> |
26 | * @category File |
27 | * @copyright David Grant <david@grant.org.uk>, Tim Starling <tstarling@wikimedia.org> |
28 | * @license http://www.gnu.org/copyleft/lesser.html GNU LGPL |
29 | * @link http://pear.php.net/package/File_Ogg |
30 | * @package File_Ogg |
31 | * @version CVS: $Id: Ogg.php,v 1.14 2005/11/19 09:06:30 djg Exp $ |
32 | */ |
33 | |
34 | /** |
35 | * @access public |
36 | */ |
37 | |
38 | use MediaWiki\TimedMediaHandler\Handlers\OggHandler\OggException; |
39 | |
40 | define("OGG_STREAM_VORBIS", 1); |
41 | /** |
42 | * @access public |
43 | */ |
44 | define("OGG_STREAM_THEORA", 2); |
45 | /** |
46 | * @access public |
47 | */ |
48 | define("OGG_STREAM_SPEEX", 3); |
49 | /** |
50 | * @access public |
51 | */ |
52 | define("OGG_STREAM_FLAC", 4); |
53 | /** |
54 | * @access public |
55 | */ |
56 | define("OGG_STREAM_OPUS", 5); |
57 | |
58 | /** |
59 | * Capture pattern to determine if a file is an Ogg physical stream. |
60 | * |
61 | * @access private |
62 | */ |
63 | define("OGG_CAPTURE_PATTERN", "OggS"); |
64 | /** |
65 | * Maximum size of an Ogg stream page plus four. This value is specified to allow |
66 | * efficient parsing of the physical stream. The extra four is a paranoid measure |
67 | * to make sure a capture pattern is not split into two parts accidentally. |
68 | * |
69 | * @access private |
70 | */ |
71 | define("OGG_MAXIMUM_PAGE_SIZE", 65311); |
72 | /** |
73 | * Capture pattern for an Ogg Vorbis logical stream. |
74 | * |
75 | * @access private |
76 | */ |
77 | define("OGG_STREAM_CAPTURE_VORBIS", "vorbis"); |
78 | /** |
79 | * Capture pattern for an Ogg Speex logical stream. |
80 | * @access private |
81 | */ |
82 | define("OGG_STREAM_CAPTURE_SPEEX", "Speex "); |
83 | /** |
84 | * Capture pattern for an Ogg FLAC logical stream. |
85 | * |
86 | * @access private |
87 | */ |
88 | define("OGG_STREAM_CAPTURE_FLAC", "FLAC"); |
89 | /** |
90 | * Capture pattern for an Ogg Theora logical stream. |
91 | * |
92 | * @access private |
93 | */ |
94 | define("OGG_STREAM_CAPTURE_THEORA", "theora"); |
95 | /** |
96 | * Capture pattern for an Ogg Opus logical stream. |
97 | * @access private |
98 | */ |
99 | define("OGG_STREAM_CAPTURE_OPUS", "OpusHead"); |
100 | /** |
101 | * Error thrown if the file location passed is nonexistant or unreadable. |
102 | * |
103 | * @access private |
104 | */ |
105 | define("OGG_ERROR_INVALID_FILE", 1); |
106 | /** |
107 | * Error thrown if the user attempts to extract an unsupported logical stream. |
108 | * |
109 | * @access private |
110 | */ |
111 | define("OGG_ERROR_UNSUPPORTED", 2); |
112 | /** |
113 | * Error thrown if the user attempts to extract an logical stream with no |
114 | * corresponding serial number. |
115 | * |
116 | * @access private |
117 | */ |
118 | define("OGG_ERROR_BAD_SERIAL", 3); |
119 | /** |
120 | * Error thrown if the stream appears to be corrupted. |
121 | * |
122 | * @access private |
123 | */ |
124 | define("OGG_ERROR_UNDECODABLE", 4); |
125 | |
126 | |
127 | /** |
128 | * Class for parsing a ogg bitstream. |
129 | * |
130 | * This class provides a means to access several types of logical bitstreams (e.g. Vorbis) |
131 | * within a Ogg physical bitstream. |
132 | * |
133 | * @link http://www.xiph.org/ogg/doc/ |
134 | * @package File_Ogg |
135 | */ |
136 | class File_Ogg |
137 | { |
138 | /** |
139 | * File pointer to Ogg container. |
140 | * |
141 | * This is the file pointer used for extracting data from the Ogg stream. It is |
142 | * the result of a standard fopen call. |
143 | * |
144 | * @var resource |
145 | * @access private |
146 | */ |
147 | var $_filePointer; |
148 | |
149 | /** |
150 | * The container for all logical streams. |
151 | * |
152 | * List of all of the unique streams in the Ogg physical stream. The key |
153 | * used is the unique serial number assigned to the logical stream by the |
154 | * encoding application. |
155 | * |
156 | * @var array |
157 | * @access private |
158 | */ |
159 | var $_streamList = array(); |
160 | var $_streams = array(); |
161 | |
162 | /** |
163 | * Length in seconds of each stream group |
164 | */ |
165 | var $_groupLengths = array(); |
166 | |
167 | /** |
168 | * Total length in seconds of the entire file |
169 | */ |
170 | var $_totalLength; |
171 | var $_startOffset = false; |
172 | |
173 | /** |
174 | * Maximum number of pages to store detailed metadata for, per stream. |
175 | * We can't store every page because there could be millions, causing an OOM. |
176 | * This must be big enough so that all the codecs can get the metadata they |
177 | * need without re-reading the file. |
178 | */ |
179 | var $_maxPageCacheSize = 4; |
180 | |
181 | /** |
182 | * Returns an interface to an Ogg physical stream. |
183 | * |
184 | * This method takes the path to a local file and examines it for a physical |
185 | * ogg bitstream. After instantiation, the user should query the object for |
186 | * the logical bitstreams held within the ogg container. |
187 | * |
188 | * @access public |
189 | * @param string $fileLocation The path of the file to be examined. |
190 | */ |
191 | function __construct($fileLocation) |
192 | { |
193 | clearstatcache(); |
194 | if (! file_exists($fileLocation)) { |
195 | throw new OggException("Couldn't Open File. Check File Path.", OGG_ERROR_INVALID_FILE); |
196 | } |
197 | |
198 | // Open this file as a binary, and split the file into streams. |
199 | $this->_filePointer = fopen($fileLocation, "rb"); |
200 | if (!$this->_filePointer) |
201 | throw new OggException("Couldn't Open File. Check File Permissions.", OGG_ERROR_INVALID_FILE); |
202 | |
203 | // Check for a stream at the start |
204 | $magic = fread($this->_filePointer, strlen(OGG_CAPTURE_PATTERN)); |
205 | if ($magic !== OGG_CAPTURE_PATTERN) { |
206 | throw new OggException("Couldn't read file: Incorrect magic number.", OGG_ERROR_UNDECODABLE); |
207 | } |
208 | fseek($this->_filePointer, 0, SEEK_SET); |
209 | |
210 | $this->_splitStreams(); |
211 | fclose($this->_filePointer); |
212 | } |
213 | |
214 | /** |
215 | * Little-endian equivalent for bin2hex |
216 | * @static |
217 | */ |
218 | static function _littleEndianBin2Hex( $bin ) { |
219 | $bigEndian = bin2hex( $bin ); |
220 | // Reverse entire string |
221 | $reversed = strrev( $bigEndian ); |
222 | // Swap nibbles back |
223 | for ( $i = 0; $i < strlen( $bigEndian ); $i += 2 ) { |
224 | $temp = $reversed[$i]; |
225 | $reversed[$i] = $reversed[$i+1]; |
226 | $reversed[$i+1] = $temp; |
227 | } |
228 | return $reversed; |
229 | } |
230 | |
231 | |
232 | /** |
233 | * Read a binary structure from a file. An array of unsigned integers are read. |
234 | * Large integers are upgraded to floating point on overflow. |
235 | * |
236 | * Format is big-endian as per Theora bit packing convention, this function |
237 | * won't work for Vorbis. |
238 | * |
239 | * @param resource $file |
240 | * @param array $fields Associative array mapping name to length in bits |
241 | */ |
242 | static function _readBigEndian($file, $fields) |
243 | { |
244 | $bufferLength = ceil(array_sum($fields) / 8); |
245 | $buffer = fread($file, $bufferLength); |
246 | if (strlen($buffer) != $bufferLength) { |
247 | throw new OggException('Unexpected end of file', OGG_ERROR_UNDECODABLE); |
248 | } |
249 | $bytePos = 0; |
250 | $bitPos = 0; |
251 | $byteValue = ord($buffer[0]); |
252 | $output = array(); |
253 | foreach ($fields as $name => $width) { |
254 | if ($width % 8 == 0 && $bitPos == 0) { |
255 | // Byte aligned case |
256 | $bytes = $width / 8; |
257 | $endBytePos = $bytePos + $bytes; |
258 | $value = 0; |
259 | while ($bytePos < $endBytePos) { |
260 | $value = ($value * 256) + ord($buffer[$bytePos]); |
261 | $bytePos++; |
262 | } |
263 | if ($bytePos < strlen($buffer)) { |
264 | $byteValue = ord($buffer[$bytePos]); |
265 | } |
266 | } else { |
267 | // General case |
268 | $bitsRemaining = $width; |
269 | $value = 0; |
270 | while ($bitsRemaining > 0) { |
271 | $bitsToRead = min($bitsRemaining, 8 - $bitPos); |
272 | $byteValue <<= $bitsToRead; |
273 | $overflow = ($byteValue & 0xff00) >> 8; |
274 | $byteValue &= $byteValue & 0xff; |
275 | |
276 | $bitPos += $bitsToRead; |
277 | $bitsRemaining -= $bitsToRead; |
278 | $value += $overflow * pow(2, $bitsRemaining); |
279 | |
280 | if ($bitPos >= 8) { |
281 | $bitPos = 0; |
282 | $bytePos++; |
283 | if ($bitsRemaining <= 0) { |
284 | break; |
285 | } |
286 | $byteValue = ord($buffer[$bytePos]); |
287 | } |
288 | } |
289 | } |
290 | $output[$name] = $value; |
291 | assert($bytePos <= $bufferLength); |
292 | } |
293 | return $output; |
294 | } |
295 | |
296 | /** |
297 | * Read a binary structure from a file. An array of unsigned integers are read. |
298 | * Large integers are upgraded to floating point on overflow. |
299 | * |
300 | * Format is little-endian as per Vorbis bit packing convention. |
301 | * |
302 | * @param resource $file |
303 | * @param array $fields Associative array mapping name to length in bits |
304 | */ |
305 | static function _readLittleEndian( $file, $fields ) { |
306 | $bufferLength = ceil(array_sum($fields) / 8); |
307 | $buffer = fread($file, $bufferLength); |
308 | if (strlen($buffer) != $bufferLength) { |
309 | throw new OggException('Unexpected end of file', OGG_ERROR_UNDECODABLE); |
310 | } |
311 | |
312 | $bytePos = 0; |
313 | $bitPos = 0; |
314 | $byteValue = ord($buffer[0]) << 8; |
315 | $output = array(); |
316 | foreach ($fields as $name => $width) { |
317 | if ($width % 8 == 0 && $bitPos == 0) { |
318 | // Byte aligned case |
319 | $bytes = $width / 8; |
320 | $value = 0; |
321 | for ($i = 0; $i < $bytes; $i++, $bytePos++) { |
322 | $value += pow(256, $i) * ord($buffer[$bytePos]); |
323 | } |
324 | if ($bytePos < strlen($buffer)) { |
325 | $byteValue = ord($buffer[$bytePos]) << 8; |
326 | } |
327 | } else { |
328 | // General case |
329 | $bitsRemaining = $width; |
330 | $value = 0; |
331 | while ($bitsRemaining > 0) { |
332 | $bitsToRead = min($bitsRemaining, 8 - $bitPos); |
333 | $byteValue >>= $bitsToRead; |
334 | $overflow = ($byteValue & 0xff) >> (8 - $bitsToRead); |
335 | $byteValue &= 0xff00; |
336 | |
337 | $value += $overflow * pow(2, $width - $bitsRemaining); |
338 | $bitPos += $bitsToRead; |
339 | $bitsRemaining -= $bitsToRead; |
340 | |
341 | if ($bitPos >= 8) { |
342 | $bitPos = 0; |
343 | $bytePos++; |
344 | if ($bitsRemaining <= 0) { |
345 | break; |
346 | } |
347 | $byteValue = ord($buffer[$bytePos]) << 8; |
348 | } |
349 | } |
350 | } |
351 | $output[$name] = $value; |
352 | assert($bytePos <= $bufferLength); |
353 | } |
354 | return $output; |
355 | } |
356 | |
357 | |
358 | /** |
359 | * @access private |
360 | */ |
361 | function _decodePageHeader($pageData, $pageOffset, $groupId) |
362 | { |
363 | // Don't blindly substr() and unpack() if data is cut off |
364 | if (strlen($pageData) < 27) |
365 | return (false); |
366 | |
367 | // Extract the various bits and pieces found in each packet header. |
368 | if (substr($pageData, 0, 4) != OGG_CAPTURE_PATTERN) |
369 | return (false); |
370 | |
371 | $stream_version = unpack("C1data", substr($pageData, 4, 1)); |
372 | if ($stream_version['data'] != 0x00) |
373 | return (false); |
374 | |
375 | $header_flag = unpack("Cdata", substr($pageData, 5, 1)); |
376 | |
377 | // Exact granule position |
378 | $abs_granule_pos = self::_littleEndianBin2Hex( substr($pageData, 6, 8)); |
379 | |
380 | // Approximate (floating point) granule position |
381 | $pos = unpack("Va/Vb", substr($pageData, 6, 8)); |
382 | $approx_granule_pos = $pos['a'] + $pos['b'] * pow(2, 32); |
383 | |
384 | // Serial number for the current datastream. |
385 | $stream_serial = unpack("Vdata", substr($pageData, 14, 4)); |
386 | $page_sequence = unpack("Vdata", substr($pageData, 18, 4)); |
387 | $checksum = unpack("Vdata", substr($pageData, 22, 4)); |
388 | $page_segments = unpack("Cdata", substr($pageData, 26, 1)); |
389 | |
390 | // Header is extended with segment lengths; make sure we have data. |
391 | if (strlen($pageData) < 27 + $page_segments['data']) |
392 | return (false); |
393 | |
394 | $segments_total = 0; |
395 | for ($i = 0; $i < $page_segments['data']; ++$i) { |
396 | $segment_length = unpack("Cdata", substr($pageData, 26 + ($i + 1), 1)); |
397 | $segments_total += $segment_length['data']; |
398 | } |
399 | $pageFinish = $pageOffset + 27 + $page_segments['data'] + $segments_total; |
400 | $page = array( |
401 | 'stream_version' => $stream_version['data'], |
402 | 'header_flag' => $header_flag['data'], |
403 | 'abs_granule_pos' => $abs_granule_pos, |
404 | 'approx_granule_pos' => $approx_granule_pos, |
405 | 'checksum' => sprintf("%u", $checksum['data']), |
406 | 'segments' => $page_segments['data'], |
407 | 'head_offset' => $pageOffset, |
408 | 'body_offset' => $pageOffset + 27 + $page_segments['data'], |
409 | 'body_finish' => $pageFinish, |
410 | 'data_length' => $pageFinish - $pageOffset, |
411 | 'group' => $groupId, |
412 | ); |
413 | if ( !isset( $this->_streamList[$stream_serial['data']] ) ) { |
414 | $this->_streamList[$stream_serial['data']] = array( |
415 | 'pages' => array(), |
416 | 'data_length' => 0, |
417 | 'first_granule_pos' => null, |
418 | 'last_granule_pos' => null, |
419 | ); |
420 | } |
421 | $stream =& $this->_streamList[$stream_serial['data']]; |
422 | if ( count( $stream['pages'] ) < $this->_maxPageCacheSize ) { |
423 | $stream['pages'][$page_sequence['data']] = $page; |
424 | } |
425 | $stream['last_page'] = $page; |
426 | $stream['data_length'] += $page['data_length']; |
427 | |
428 | # Reject -1 as a granule pos, that means no segment finished in the packet |
429 | if ( $abs_granule_pos !== 'ffffffffffffffff' ) { |
430 | if ( $stream['first_granule_pos'] === null ) { |
431 | $stream['first_granule_pos'] = $abs_granule_pos; |
432 | } |
433 | $stream['last_granule_pos'] = $abs_granule_pos; |
434 | } |
435 | |
436 | $pageData = null; |
437 | return $page; |
438 | } |
439 | |
440 | /** |
441 | * @access private |
442 | */ |
443 | function _splitStreams() |
444 | { |
445 | // Loop through the physical stream until there are no more pages to read. |
446 | $groupId = 0; |
447 | $openStreams = 0; |
448 | $this_page_offset = 0; |
449 | while (!feof($this->_filePointer)) { |
450 | $pageData = fread($this->_filePointer, 282); |
451 | if (strval($pageData) === '') { |
452 | break; |
453 | } |
454 | $page = $this->_decodePageHeader($pageData, $this_page_offset, $groupId); |
455 | if ($page === false) { |
456 | throw new OggException("Cannot decode Ogg file: Invalid page at offset $this_page_offset", OGG_ERROR_UNDECODABLE); |
457 | } |
458 | |
459 | // Keep track of multiplexed groups |
460 | if ($page['header_flag'] & 2/*bos*/) { |
461 | $openStreams++; |
462 | } elseif ($page['header_flag'] & 4/*eos*/) { |
463 | $openStreams--; |
464 | if (!$openStreams) { |
465 | // End of group |
466 | $groupId++; |
467 | } |
468 | } |
469 | if ($openStreams < 0) { |
470 | throw new OggException("Unexpected end of stream", OGG_ERROR_UNDECODABLE); |
471 | } |
472 | |
473 | $this_page_offset = $page['body_finish']; |
474 | fseek( $this->_filePointer, $this_page_offset, SEEK_SET ); |
475 | } |
476 | // Loop through the streams, and find out what type of stream is available. |
477 | $groupLengths = array(); |
478 | foreach ($this->_streamList as $stream_serial => $streamData) { |
479 | // @phan-suppress-next-line PhanTypeArraySuspiciousNullable False positive |
480 | fseek($this->_filePointer, $streamData['pages'][0]['body_offset'], SEEK_SET); |
481 | $pattern = fread($this->_filePointer, 8); |
482 | if (preg_match("/" . OGG_STREAM_CAPTURE_VORBIS . "/", $pattern)) { |
483 | $this->_streamList[$stream_serial]['stream_type'] = OGG_STREAM_VORBIS; |
484 | $stream = new File_Ogg_Vorbis($stream_serial, $streamData, $this->_filePointer); |
485 | } elseif (preg_match("/" . OGG_STREAM_CAPTURE_SPEEX . "/", $pattern)) { |
486 | $this->_streamList[$stream_serial]['stream_type'] = OGG_STREAM_SPEEX; |
487 | $stream = new File_Ogg_Speex($stream_serial, $streamData, $this->_filePointer); |
488 | } elseif (preg_match("/" . OGG_STREAM_CAPTURE_FLAC . "/", $pattern)) { |
489 | $this->_streamList[$stream_serial]['stream_type'] = OGG_STREAM_FLAC; |
490 | $stream = new File_Ogg_Flac($stream_serial, $streamData, $this->_filePointer); |
491 | } elseif (preg_match("/" . OGG_STREAM_CAPTURE_THEORA . "/", $pattern)) { |
492 | $this->_streamList[$stream_serial]['stream_type'] = OGG_STREAM_THEORA; |
493 | $stream = new File_Ogg_Theora($stream_serial, $streamData, $this->_filePointer); |
494 | } elseif (preg_match("/" . OGG_STREAM_CAPTURE_OPUS . "/", $pattern)) { |
495 | $this->_streamList[$stream_serial]['stream_type'] = OGG_STREAM_OPUS; |
496 | $stream = new File_Ogg_Opus($stream_serial, $streamData, $this->_filePointer); |
497 | } else { |
498 | $streamData['stream_type'] = "unknown"; |
499 | $stream = false; |
500 | } |
501 | |
502 | if ($stream) { |
503 | $this->_streams[$stream_serial] = $stream; |
504 | // @phan-suppress-next-line PhanTypeArraySuspiciousNullable False positive |
505 | $group = $streamData['pages'][0]['group']; |
506 | if (isset($groupLengths[$group])) { |
507 | $groupLengths[$group] = max($groupLengths[$group], $stream->getLength()); |
508 | } else { |
509 | $groupLengths[$group] = $stream->getLength(); |
510 | } |
511 | //just store the startOffset for the first stream: |
512 | if( $this->_startOffset === false ){ |
513 | $this->_startOffset = $stream->getStartOffset(); |
514 | } |
515 | |
516 | } |
517 | } |
518 | $this->_groupLengths = $groupLengths; |
519 | $this->_totalLength = array_sum( $groupLengths ); |
520 | $this->_streamList = []; |
521 | } |
522 | |
523 | /** |
524 | * Returns the appropriate logical bitstream that corresponds to the provided serial. |
525 | * |
526 | * This function returns a logical bitstream contained within the Ogg physical |
527 | * stream, corresponding to the serial used as the offset for that bitstream. |
528 | * The returned stream may be Vorbis, Speex, FLAC or Theora, although the only |
529 | * usable bitstream is Vorbis. |
530 | * |
531 | * @return File_Ogg_Bitstream |
532 | */ |
533 | function &getStream($streamSerial) |
534 | { |
535 | if (! array_key_exists($streamSerial, $this->_streams)) |
536 | throw new OggException("The stream number is invalid.", OGG_ERROR_BAD_SERIAL); |
537 | |
538 | return $this->_streams[$streamSerial]; |
539 | } |
540 | |
541 | /** |
542 | * Returns an array of logical streams inside this physical bitstream. |
543 | * |
544 | * This function returns an array of logical streams found within this physical |
545 | * bitstream. If a filter is provided, only logical streams of the requested type |
546 | * are returned, as an array of serial numbers. If no filter is provided, this |
547 | * function returns a two-dimensional array, with the stream type as the primary key, |
548 | * and a value consisting of an array of stream serial numbers. |
549 | * |
550 | * @param int $filter |
551 | * @return array |
552 | */ |
553 | function listStreams($filter = null) |
554 | { |
555 | $streams = array(); |
556 | // Loops through the streams and assign them to an appropriate index, |
557 | // ready for filtering the second part of this function. |
558 | foreach ($this->_streams as $serial => $stream) { |
559 | $stream_type = 0; |
560 | switch (get_class($stream)) { |
561 | case "file_ogg_flac": |
562 | $stream_type = OGG_STREAM_FLAC; |
563 | break; |
564 | case "file_ogg_speex": |
565 | $stream_type = OGG_STREAM_SPEEX; |
566 | break; |
567 | case "file_ogg_theora": |
568 | $stream_type = OGG_STREAM_THEORA; |
569 | break; |
570 | case "file_ogg_vorbis": |
571 | $stream_type = OGG_STREAM_VORBIS; |
572 | break; |
573 | } |
574 | if (! isset($streams[$stream_type])) |
575 | // Initialise the result list for this stream type. |
576 | $streams[$stream_type] = array(); |
577 | |
578 | $streams[$stream_type][] = $serial; |
579 | } |
580 | |
581 | // Perform filtering. |
582 | if (is_null($filter)) |
583 | return ($streams); |
584 | elseif (isset($streams[$filter])) |
585 | return ($streams[$filter]); |
586 | else |
587 | return array(); |
588 | } |
589 | /** |
590 | * getStartOffset |
591 | * |
592 | * @return int |
593 | */ |
594 | function getStartOffset(){ |
595 | if( $this->_startOffset === false) |
596 | return 0; |
597 | return $this->_startOffset; |
598 | } |
599 | /** |
600 | * Get the total length of the group of streams |
601 | */ |
602 | function getLength() { |
603 | return $this->_totalLength; |
604 | } |
605 | } |
606 | ?> |