Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
0.00% |
0 / 91 |
|
0.00% |
0 / 31 |
CRAP | |
0.00% |
0 / 1 |
File_Ogg_Vorbis | |
0.00% |
0 / 83 |
|
0.00% |
0 / 31 |
1806 | |
0.00% |
0 / 1 |
__construct | |
0.00% |
0 / 10 |
|
0.00% |
0 / 1 |
12 | |||
getSecondsFromGranulePos | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
2 | |||
getType | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
_decodeIdentificationHeader | |
0.00% |
0 / 34 |
|
0.00% |
0 / 1 |
56 | |||
_decodeCommentsHeader | |
0.00% |
0 / 5 |
|
0.00% |
0 / 1 |
6 | |||
getIdentificationString | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getEncoderVersion | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getSampleRate | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getBitrates | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getBitrate | |
0.00% |
0 / 5 |
|
0.00% |
0 / 1 |
12 | |||
getLength | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getStartOffset | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
isMono | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
isStereo | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
isQuadrophonic | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getTitle | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getVersion | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getAlbum | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getTrackNumber | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getArtist | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getPerformer | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getCopyright | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getLicense | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getOrganization | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getDescription | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getGenre | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getDate | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getLocation | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getContact | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getIsrc | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getHeader | |
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 | /** |
26 | * Check number for the first header in a Vorbis stream. |
27 | * |
28 | * @access private |
29 | */ |
30 | |
31 | use MediaWiki\TimedMediaHandler\Handlers\OggHandler\OggException; |
32 | |
33 | define("OGG_VORBIS_IDENTIFICATION_HEADER", 1); |
34 | /** |
35 | * Check number for the second header in a Vorbis stream. |
36 | * |
37 | * @access private |
38 | */ |
39 | define("OGG_VORBIS_COMMENTS_HEADER", 3); |
40 | /** |
41 | * Check number for the third header in a Vorbis stream. |
42 | * |
43 | * @access private |
44 | */ |
45 | define("OGG_VORBIS_SETUP_HEADER", 5); |
46 | /** |
47 | * Error thrown if the stream appears to be corrupted. |
48 | * |
49 | * @access private |
50 | */ |
51 | define("OGG_VORBIS_ERROR_UNDECODABLE", OGG_ERROR_UNDECODABLE); |
52 | /** |
53 | * Error thrown if the user attempts to extract a comment using a comment key |
54 | * that does not exist. |
55 | * |
56 | * @access private |
57 | */ |
58 | define("OGG_VORBIS_ERROR_INVALID_COMMENT", 2); |
59 | |
60 | define("OGG_VORBIS_IDENTIFICATION_PAGE_OFFSET", 0); |
61 | define("OGG_VORBIS_COMMENTS_PAGE_OFFSET", 1); |
62 | |
63 | /** |
64 | * Error thrown if the user attempts to write a comment containing an illegal |
65 | * character |
66 | * |
67 | * @access private |
68 | */ |
69 | define("OGG_VORBIS_ERROR_ILLEGAL_COMMENT", 3); |
70 | |
71 | /** |
72 | * Extract the contents of a Vorbis logical stream. |
73 | * |
74 | * This class provides an interface to a Vorbis logical stream found within |
75 | * a Ogg stream. A variety of information may be extracted, including comment |
76 | * tags, running time, and bitrate. For more information, please see the following |
77 | * links. |
78 | * |
79 | * @author David Grant <david@grant.org.uk>, Tim Starling <tstarling@wikimedia.org> |
80 | * @category File |
81 | * @copyright David Grant <david@grant.org.uk>, Tim Starling <tstarling@wikimedia.org> |
82 | * @license http://www.gnu.org/copyleft/lesser.html GNU LGPL |
83 | * @link http://pear.php.net/package/File_Ogg |
84 | * @link http://www.xiph.org/vorbis/doc/ |
85 | * @package File_Ogg |
86 | * @version CVS: $Id: Vorbis.php,v 1.13 2005/11/19 09:06:32 djg Exp $ |
87 | */ |
88 | class File_Ogg_Vorbis extends File_Ogg_Media |
89 | { |
90 | |
91 | /** |
92 | * Version of vorbis specification used. |
93 | * |
94 | * @access private |
95 | * @var int |
96 | */ |
97 | var $_version; |
98 | |
99 | /** |
100 | * Number of channels in the vorbis stream. |
101 | * |
102 | * @access private |
103 | * @var int |
104 | */ |
105 | var $_channels; |
106 | |
107 | /** |
108 | * Vorbis Identification Header |
109 | * https://xiph.org/vorbis/doc/Vorbis_I_spec.html#x1-610004.2 |
110 | * |
111 | * @access private |
112 | * @var array |
113 | */ |
114 | var $_idHeader; |
115 | |
116 | /** |
117 | * Number of samples per second in the vorbis stream. |
118 | * |
119 | * @access private |
120 | * @var int |
121 | */ |
122 | var $_sampleRate; |
123 | |
124 | /** |
125 | * Minimum bitrate for the vorbis stream. |
126 | * |
127 | * @access private |
128 | * @var int |
129 | */ |
130 | var $_minBitrate; |
131 | |
132 | /** |
133 | * Maximum bitrate for the vorbis stream. |
134 | * |
135 | * @access private |
136 | * @var int |
137 | */ |
138 | var $_maxBitrate; |
139 | |
140 | /** |
141 | * Nominal bitrate for the vorbis stream. |
142 | * |
143 | * @access private |
144 | * @var int |
145 | */ |
146 | var $_nomBitrate; |
147 | |
148 | /** |
149 | * The length of this stream in seconds. |
150 | * |
151 | * @access private |
152 | * @var int |
153 | */ |
154 | var $_streamLength; |
155 | |
156 | /** |
157 | * the start offset of this stream in seconds |
158 | */ |
159 | var $_startOffset; |
160 | /** |
161 | * Constructor for accessing a Vorbis logical stream. |
162 | * |
163 | * This method is the constructor for the native-PHP interface to a Vorbis logical |
164 | * stream, embedded within an Ogg physical stream. |
165 | * |
166 | * @param int $streamSerial Serial number of the logical stream. |
167 | * @param array $streamData Data for the requested logical stream. |
168 | * @param resource $filePointer File pointer for the current physical stream. |
169 | * @access private |
170 | */ |
171 | function __construct($streamSerial, $streamData, $filePointer) |
172 | { |
173 | parent::__construct($streamSerial, $streamData, $filePointer); |
174 | $this->_decodeIdentificationHeader(); |
175 | $this->_decodeCommentsHeader(OGG_VORBIS_COMMENTS_HEADER, OGG_VORBIS_COMMENTS_PAGE_OFFSET); |
176 | |
177 | $endSec = $this->getSecondsFromGranulePos( $this->_lastGranulePos ); |
178 | $startSec = $this->getSecondsFromGranulePos( $this->_firstGranulePos ); |
179 | |
180 | //make sure the offset is worth taking into account oggz_chop related hack |
181 | if( $startSec > 1){ |
182 | $this->_streamLength = $endSec - $startSec; |
183 | $this->_startOffset = $startSec; |
184 | }else{ |
185 | $this->_streamLength = $endSec; |
186 | } |
187 | |
188 | $this->_avgBitrate = $this->_streamLength ? ($this->_streamSize * 8) / $this->_streamLength : 0; |
189 | } |
190 | function getSecondsFromGranulePos( $granulePos ){ |
191 | return (intval(substr( $granulePos, 0, 8 ), 16) * pow(2, 32) |
192 | + intval( substr( $granulePos, 8, 8 ), 16 )) |
193 | / $this->_idHeader['audio_sample_rate']; |
194 | } |
195 | /** |
196 | * Get a short string describing the type of the stream |
197 | */ |
198 | function getType() |
199 | { |
200 | return 'Vorbis'; |
201 | } |
202 | |
203 | /** |
204 | * Parse the identification header (the first of three headers) in a Vorbis stream. |
205 | * |
206 | * This function parses the identification header. The identification header |
207 | * contains simple audio characteristics, such as sample rate and number of |
208 | * channels. There are a number of error-checking provisions laid down in the Vorbis |
209 | * specification to ensure the stream is pure. |
210 | * |
211 | * @access private |
212 | */ |
213 | function _decodeIdentificationHeader() |
214 | { |
215 | $this->_decodeCommonHeader(OGG_VORBIS_IDENTIFICATION_HEADER, OGG_VORBIS_IDENTIFICATION_PAGE_OFFSET); |
216 | |
217 | $h = File_Ogg::_readLittleEndian($this->_filePointer, array( |
218 | 'vorbis_version' => 32, |
219 | 'audio_channels' => 8, |
220 | 'audio_sample_rate' => 32, |
221 | 'bitrate_maximum' => 32, |
222 | 'bitrate_nominal' => 32, |
223 | 'bitrate_minimum' => 32, |
224 | 'blocksize_0' => 4, |
225 | 'blocksize_1' => 4, |
226 | 'framing_flag' => 1 |
227 | )); |
228 | |
229 | // The Vorbis stream version must be 0. |
230 | if ($h['vorbis_version'] == 0) |
231 | $this->_version = $h['vorbis_version']; |
232 | else |
233 | throw new OggException("Stream is undecodable due to an invalid vorbis stream version.", OGG_VORBIS_ERROR_UNDECODABLE); |
234 | |
235 | // The number of channels MUST be greater than 0. |
236 | if ($h['audio_channels'] == 0) |
237 | throw new OggException("Stream is undecodable due to zero channels.", OGG_VORBIS_ERROR_UNDECODABLE); |
238 | else |
239 | $this->_channels = $h['audio_channels']; |
240 | |
241 | // The sample rate MUST be greater than 0. |
242 | if ($h['audio_sample_rate'] == 0) |
243 | throw new OggException("Stream is undecodable due to a zero sample rate.", OGG_VORBIS_ERROR_UNDECODABLE); |
244 | else |
245 | $this->_sampleRate = $h['audio_sample_rate']; |
246 | |
247 | // Extract the various bitrates |
248 | $this->_maxBitrate = $h['bitrate_maximum']; |
249 | $this->_nomBitrate = $h['bitrate_nominal']; |
250 | $this->_minBitrate = $h['bitrate_minimum']; |
251 | |
252 | // Powers of two between 6 and 13 inclusive. |
253 | $valid_block_sizes = array(64, 128, 256, 512, 1024, 2048, 4096, 8192); |
254 | |
255 | // blocksize_0 MUST be a valid blocksize. |
256 | $blocksize_0 = pow(2, $h['blocksize_0']); |
257 | if (!in_array($blocksize_0, $valid_block_sizes)) |
258 | throw new OggException("Stream is undecodable because blocksize_0 is $blocksize_0, which is not a valid size.", OGG_VORBIS_ERROR_UNDECODABLE); |
259 | |
260 | // Extract bits 5 to 8 from the character data. |
261 | // blocksize_1 MUST be a valid blocksize. |
262 | $blocksize_1 = pow(2, $h['blocksize_1']); |
263 | if (!in_array($blocksize_1, $valid_block_sizes)) |
264 | throw new OggException("Stream is undecodable because blocksize_1 is not a valid size.", OGG_VORBIS_ERROR_UNDECODABLE); |
265 | |
266 | // blocksize 0 MUST be less than or equal to blocksize 1. |
267 | if ($blocksize_0 > $blocksize_1) |
268 | throw new OggException("Stream is undecodable because blocksize_0 is not less than or equal to blocksize_1.", OGG_VORBIS_ERROR_UNDECODABLE); |
269 | |
270 | // The framing bit MUST be set to mark the end of the identification header. |
271 | // Some encoders are broken though -- TS |
272 | /* |
273 | if ($h['framing_flag'] == 0) |
274 | throw new OggException("Stream in undecodable because the framing bit is not non-zero.", OGG_VORBIS_ERROR_UNDECODABLE); |
275 | */ |
276 | |
277 | $this->_idHeader = $h; |
278 | } |
279 | |
280 | /** |
281 | * Decode the comments header |
282 | * @access private |
283 | * @param int $packetType |
284 | * @param int $pageOffset |
285 | */ |
286 | function _decodeCommentsHeader($packetType, $pageOffset) |
287 | { |
288 | $this->_decodeCommonHeader($packetType, $pageOffset); |
289 | $this->_decodeBareCommentsHeader(); |
290 | // The framing bit MUST be set to mark the end of the comments header. |
291 | $framing_bit = unpack("Cdata", fread($this->_filePointer, 1)); |
292 | if ($framing_bit['data'] != 1) |
293 | throw new OggException("Stream Undecodable", OGG_VORBIS_ERROR_UNDECODABLE); |
294 | } |
295 | |
296 | /** |
297 | * Get the 6-byte identification string expected in the common header |
298 | */ |
299 | function getIdentificationString() { |
300 | return OGG_STREAM_CAPTURE_VORBIS; |
301 | } |
302 | |
303 | /** |
304 | * Version of the Vorbis specification referred to in the encoding of this stream. |
305 | * |
306 | * This method returns the version of the Vorbis specification (currently 0 (ZERO)) |
307 | * referred to by the encoder of this stream. The Vorbis specification is well- |
308 | * defined, and thus one does not expect this value to change on a frequent basis. |
309 | * |
310 | * @access public |
311 | * @return int |
312 | */ |
313 | function getEncoderVersion() |
314 | { |
315 | return ($this->_version); |
316 | } |
317 | |
318 | /** |
319 | * Samples per second. |
320 | * |
321 | * This function returns the number of samples used per second in this |
322 | * recording. Probably the most common value here is 44,100. |
323 | * |
324 | * @return int |
325 | * @access public |
326 | */ |
327 | function getSampleRate() |
328 | { |
329 | return ($this->_sampleRate); |
330 | } |
331 | |
332 | /** |
333 | * Various bitrate measurements |
334 | * |
335 | * Gives an array of the values of four different types of bitrates for this |
336 | * stream. The nominal, maximum and minimum values are found within the file, |
337 | * whereas the average value is computed. |
338 | * |
339 | * @access public |
340 | * @return array |
341 | */ |
342 | function getBitrates() |
343 | { |
344 | return (array("nom" => $this->_nomBitrate, "max" => $this->_maxBitrate, "min" => $this->_minBitrate, "avg" => $this->_avgBitrate)); |
345 | } |
346 | |
347 | /** |
348 | * Gives the most accurate bitrate measurement from this stream. |
349 | * |
350 | * This function returns the most accurate bitrate measurement for this |
351 | * recording, depending on values set in the stream header. |
352 | * |
353 | * @access public |
354 | * @return float |
355 | */ |
356 | function getBitrate() |
357 | { |
358 | if ($this->_avgBitrate != 0) |
359 | return ($this->_avgBitrate); |
360 | elseif ($this->_nomBitrate != 0) |
361 | return ($this->_nomBitrate); |
362 | else |
363 | return (($this->_minBitrate + $this->_maxBitrate) / 2); |
364 | } |
365 | |
366 | /** |
367 | * Gives the length (in seconds) of this stream. |
368 | * |
369 | * @access public |
370 | * @return int |
371 | */ |
372 | function getLength() |
373 | { |
374 | return ($this->_streamLength); |
375 | } |
376 | /** |
377 | * Get the start offset of the stream in seconds |
378 | * @access public |
379 | * @return int |
380 | */ |
381 | function getStartOffset(){ |
382 | return ($this->_startOffset); |
383 | } |
384 | /** |
385 | * States whether this logical stream was encoded in mono. |
386 | * |
387 | * @access public |
388 | * @return boolean |
389 | */ |
390 | function isMono() |
391 | { |
392 | return ($this->_channels == 1); |
393 | } |
394 | |
395 | /** |
396 | * States whether this logical stream was encoded in stereo. |
397 | * |
398 | * @access public |
399 | * @return boolean |
400 | */ |
401 | function isStereo() |
402 | { |
403 | return ($this->_channels == 2); |
404 | } |
405 | |
406 | /** |
407 | * States whether this logical stream was encoded in quadrophonic sound. |
408 | * |
409 | * @access public |
410 | * @return boolean |
411 | */ |
412 | function isQuadrophonic() |
413 | { |
414 | return ($this->_channels == 4); |
415 | } |
416 | |
417 | /** |
418 | * The title of this track, e.g. "What's Up Pussycat?". |
419 | * |
420 | * @access public |
421 | * @return string |
422 | */ |
423 | function getTitle() |
424 | { |
425 | return ($this->getField("TITLE")); |
426 | } |
427 | |
428 | /** |
429 | * The version of the track, such as a remix. |
430 | * |
431 | * @access public |
432 | * @return string |
433 | */ |
434 | function getVersion() |
435 | { |
436 | return $this->getField("VERSION"); |
437 | } |
438 | |
439 | /** |
440 | * The album or collection from which this track comes. |
441 | * |
442 | * @access public |
443 | * @return string |
444 | */ |
445 | function getAlbum() |
446 | { |
447 | return ($this->getField("ALBUM")); |
448 | } |
449 | |
450 | /** |
451 | * The number of this track if it is part of a larger collection. |
452 | * |
453 | * @access public |
454 | * @return string |
455 | */ |
456 | function getTrackNumber() |
457 | { |
458 | return ($this->getField("TRACKNUMBER")); |
459 | } |
460 | |
461 | /** |
462 | * The artist responsible for this track. |
463 | * |
464 | * This function returns the name of the artist responsible for this |
465 | * recording, which may be either a solo-artist, duet or group. |
466 | * |
467 | * @access public |
468 | * @return string |
469 | */ |
470 | function getArtist() |
471 | { |
472 | return ($this->getField("ARTIST")); |
473 | } |
474 | |
475 | /** |
476 | * The performer of this track, such as an orchestra |
477 | * |
478 | * @access public |
479 | * @return string |
480 | */ |
481 | function getPerformer() |
482 | { |
483 | return ($this->getField("PERFORMER")); |
484 | } |
485 | |
486 | /** |
487 | * The copyright attribution for this track. |
488 | * |
489 | * @access public |
490 | * @return string |
491 | */ |
492 | function getCopyright() |
493 | { |
494 | return ($this->getField("COPYRIGHT")); |
495 | } |
496 | |
497 | /** |
498 | * The rights of distribution for this track. |
499 | * |
500 | * This funtion returns the license for this track, and may include |
501 | * copyright information, or a creative commons statement. |
502 | * |
503 | * @access public |
504 | * @return string |
505 | */ |
506 | function getLicense() |
507 | { |
508 | return ($this->getField("LICENSE")); |
509 | } |
510 | |
511 | /** |
512 | * The organisation responsible for this track. |
513 | * |
514 | * This function returns the name of the organisation responsible for |
515 | * the production of this track, such as the record label. |
516 | * |
517 | * @access public |
518 | * @return string |
519 | */ |
520 | function getOrganization() |
521 | { |
522 | return ($this->getField("ORGANIZATION")); |
523 | } |
524 | |
525 | /** |
526 | * A short description of the contents of this track. |
527 | * |
528 | * This function returns a short description of this track, which might |
529 | * contain extra information that doesn't fit anywhere else. |
530 | * |
531 | * @access public |
532 | * @return string |
533 | */ |
534 | function getDescription() |
535 | { |
536 | return ($this->getField("DESCRIPTION")); |
537 | } |
538 | |
539 | /** |
540 | * The genre of this recording (e.g. Rock) |
541 | * |
542 | * This function returns the genre of this recording. There are no pre- |
543 | * defined genres, so this is completely up to the tagging software. |
544 | * |
545 | * @access public |
546 | * @return string |
547 | */ |
548 | function getGenre() |
549 | { |
550 | return ($this->getField("GENRE")); |
551 | } |
552 | |
553 | /** |
554 | * The date of the recording of this track. |
555 | * |
556 | * This function returns the date on which this recording was made. There |
557 | * is no specification for the format of this date. |
558 | * |
559 | * @access public |
560 | * @return string |
561 | */ |
562 | function getDate() |
563 | { |
564 | return ($this->getField("DATE")); |
565 | } |
566 | |
567 | /** |
568 | * Where this recording was made. |
569 | * |
570 | * This function returns where this recording was made, such as a recording |
571 | * studio, or concert venue. |
572 | * |
573 | * @access public |
574 | * @return string |
575 | */ |
576 | function getLocation() |
577 | { |
578 | return ($this->getField("LOCATION")); |
579 | } |
580 | |
581 | /** |
582 | * @access public |
583 | * @return string |
584 | */ |
585 | function getContact() |
586 | { |
587 | return ($this->getField("CONTACT")); |
588 | } |
589 | |
590 | /** |
591 | * International Standard Recording Code. |
592 | * |
593 | * Returns the International Standard Recording Code. This code can be |
594 | * validated using the Validate_ISPN package. |
595 | * |
596 | * @access public |
597 | * @return string |
598 | */ |
599 | function getIsrc() |
600 | { |
601 | return ($this->getField("ISRC")); |
602 | } |
603 | |
604 | /** |
605 | * Get an associative array containing header information about the stream |
606 | * @access public |
607 | * @return array |
608 | */ |
609 | function getHeader() { |
610 | return $this->_idHeader; |
611 | } |
612 | } |
613 | ?> |