Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
0.00% |
0 / 108 |
|
0.00% |
0 / 7 |
CRAP | |
0.00% |
0 / 1 |
File_Ogg_Theora | |
0.00% |
0 / 104 |
|
0.00% |
0 / 7 |
600 | |
0.00% |
0 / 1 |
__construct | |
0.00% |
0 / 10 |
|
0.00% |
0 / 1 |
12 | |||
getSecondsFromGranulePos | |
0.00% |
0 / 6 |
|
0.00% |
0 / 1 |
2 | |||
getIdentificationString | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
_decodeIdentificationHeader | |
0.00% |
0 / 83 |
|
0.00% |
0 / 1 |
272 | |||
getHeader | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getType | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
_decodeCommentsHeader | |
0.00% |
0 / 2 |
|
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 | use MediaWiki\TimedMediaHandler\Handlers\OggHandler\OggException; |
25 | |
26 | define( 'OGG_THEORA_IDENTIFICATION_HEADER', 0x80 ); |
27 | define( 'OGG_THEORA_COMMENTS_HEADER', 0x81 ); |
28 | define( 'OGG_THEORA_IDENTIFICATION_PAGE_OFFSET', 0 ); |
29 | define( 'OGG_THEORA_COMMENTS_PAGE_OFFSET', 1 ); |
30 | |
31 | |
32 | /** |
33 | * @author David Grant <david@grant.org.uk>, Tim Starling <tstarling@wikimedia.org> |
34 | * @category File |
35 | * @copyright David Grant <david@grant.org.uk>, Tim Starling <tstarling@wikimedia.org> |
36 | * @license http://www.gnu.org/copyleft/lesser.html GNU LGPL |
37 | * @link http://pear.php.net/package/File_Ogg |
38 | * @link http://www.xiph.org/theora/ |
39 | * @package File_Ogg |
40 | * @version CVS: $Id: Theora.php,v 1.9 2005/11/16 20:43:27 djg Exp $ |
41 | */ |
42 | class File_Ogg_Theora extends File_Ogg_Media |
43 | { |
44 | |
45 | /** |
46 | * Version of Theora as retrieved from Theora identifcation header |
47 | * |
48 | * @var string |
49 | * @access private |
50 | */ |
51 | var $_theoraVersion; |
52 | |
53 | /** |
54 | * Theora identifcation header |
55 | * |
56 | * @var array |
57 | * @access private |
58 | */ |
59 | var $_idHeader; |
60 | |
61 | /** |
62 | * Width of the encoded frame |
63 | * |
64 | * @var int |
65 | * @access private |
66 | */ |
67 | var $_frameWidth; |
68 | |
69 | /** |
70 | * Height of the encoded frame |
71 | * |
72 | * @var int |
73 | * @access private |
74 | */ |
75 | var $_frameHeight; |
76 | |
77 | /** |
78 | * Width of the displayed picture |
79 | * |
80 | * @var int |
81 | * @access private |
82 | */ |
83 | var $_pictureWidth; |
84 | |
85 | /** |
86 | * Height of the displayed picture |
87 | * |
88 | * @var int |
89 | * @access private |
90 | */ |
91 | var $_pictureHeight; |
92 | |
93 | /** |
94 | * The X offset of the displayed picture. |
95 | * |
96 | * @var int |
97 | * @access private |
98 | */ |
99 | var $_offsetX; |
100 | |
101 | /** |
102 | * The Y offset of the displayed picture. |
103 | * |
104 | * @var int |
105 | * @access private |
106 | */ |
107 | var $_offsetY; |
108 | |
109 | /** |
110 | * Frame rate for the encoded video |
111 | * |
112 | * @var double |
113 | * @access private |
114 | */ |
115 | var $_frameRate; |
116 | |
117 | /** |
118 | * Pixel Aspect Ratio |
119 | * |
120 | * @var double |
121 | * @access private |
122 | */ |
123 | var $_physicalAspectRatio; |
124 | |
125 | /** |
126 | * Color space |
127 | * |
128 | * @var string |
129 | * @access private |
130 | */ |
131 | var $_colorSpace; |
132 | |
133 | /** |
134 | * Nominal birate of the steram in bits per second |
135 | * |
136 | * @var int |
137 | * @access private |
138 | */ |
139 | var $_nomBitrate; |
140 | |
141 | /** |
142 | * Quality Hint |
143 | * |
144 | * @var int |
145 | * @access private |
146 | */ |
147 | var $_quality; |
148 | |
149 | /** |
150 | * Key frame number granule position shift |
151 | * |
152 | * @var int |
153 | * @access private |
154 | */ |
155 | var $_kfgShift; |
156 | |
157 | /** |
158 | * Pixel format |
159 | * One of 4:2:0, 4:2:2, 4:4:4 or Unknown |
160 | * |
161 | * @var string |
162 | * @access private |
163 | */ |
164 | var $_pixelFormat; |
165 | |
166 | /** |
167 | * @access private |
168 | */ |
169 | function __construct($streamSerial, $streamData, $filePointer) |
170 | { |
171 | parent::__construct($streamSerial, $streamData, $filePointer); |
172 | $this->_decodeIdentificationHeader(); |
173 | $this->_decodeCommentsHeader(); |
174 | $endSec = $this->getSecondsFromGranulePos( $this->_lastGranulePos ); |
175 | |
176 | $startSec = $this->getSecondsFromGranulePos( $this->_firstGranulePos ); |
177 | |
178 | //make sure the offset is worth taking into account oggz_chop related hack |
179 | if( $startSec > 1){ |
180 | $this->_streamLength = $endSec - $startSec; |
181 | $this->_startOffset = $startSec; |
182 | }else{ |
183 | $this->_streamLength = $endSec; |
184 | } |
185 | |
186 | $this->_avgBitrate = $this->_streamLength ? ($this->_streamSize * 8) / $this->_streamLength : 0; |
187 | } |
188 | function getSecondsFromGranulePos($granulePos){ |
189 | // Calculate GranulePos seconds |
190 | // First make some "numeric strings" |
191 | // These might not fit into PHP's integer type, but they will fit into |
192 | // the 53-bit mantissa of a double-precision number |
193 | $topWord = floatval( base_convert( substr( $granulePos, 0, 8 ), 16, 10 ) ); |
194 | $bottomWord = floatval( base_convert( substr( $granulePos, 8, 8 ), 16, 10 ) ); |
195 | // Calculate the keyframe position by shifting right by KFGSHIFT |
196 | // We don't use PHP's shift operators because they're terribly broken |
197 | // This is made slightly simpler by the fact that KFGSHIFT < 32 |
198 | $keyFramePos = $topWord / pow(2, $this->_kfgShift - 32) + |
199 | floor( $bottomWord / pow(2, $this->_kfgShift) ); |
200 | // Calculate the frame offset by masking off the top 64-KFGSHIFT bits |
201 | // This requires a bit of floating point trickery |
202 | $offset = fmod( $bottomWord, pow(2, $this->_kfgShift) ); |
203 | // They didn't teach you that one at school did they? |
204 | // Now put it together with the frame rate to calculate time in seconds |
205 | return ( $keyFramePos + $offset ) / $this->_frameRate; |
206 | } |
207 | /** |
208 | * Get the 6-byte identification string expected in the common header |
209 | */ |
210 | function getIdentificationString() |
211 | { |
212 | return OGG_STREAM_CAPTURE_THEORA; |
213 | } |
214 | |
215 | /** |
216 | * Parse the identification header in a Theora stream. |
217 | * @access private |
218 | */ |
219 | function _decodeIdentificationHeader() |
220 | { |
221 | $this->_decodeCommonHeader(OGG_THEORA_IDENTIFICATION_HEADER, OGG_THEORA_IDENTIFICATION_PAGE_OFFSET); |
222 | $h = File_Ogg::_readBigEndian( $this->_filePointer, array( |
223 | 'VMAJ' => 8, |
224 | 'VMIN' => 8, |
225 | 'VREV' => 8, |
226 | 'FMBW' => 16, |
227 | 'FMBH' => 16, |
228 | 'PICW' => 24, |
229 | 'PICH' => 24, |
230 | 'PICX' => 8, |
231 | 'PICY' => 8, |
232 | 'FRN' => 32, |
233 | 'FRD' => 32, |
234 | 'PARN' => 24, |
235 | 'PARD' => 24, |
236 | 'CS' => 8, |
237 | 'NOMBR' => 24, |
238 | 'QUAL' => 6, |
239 | 'KFGSHIFT' => 5, |
240 | 'PF' => 2)); |
241 | if ( !$h ) { |
242 | throw new OggException("Stream is undecodable due to a truncated header.", OGG_ERROR_UNDECODABLE); |
243 | } |
244 | |
245 | // Theora version |
246 | // Seems overly strict but this is what the spec says |
247 | // VREV is for backwards-compatible changes, apparently |
248 | if ( $h['VMAJ'] != 3 || $h['VMIN'] != 2 ) { |
249 | throw new OggException("Stream is undecodable due to an invalid theora version.", OGG_ERROR_UNDECODABLE); |
250 | } |
251 | $this->_theoraVersion = "{$h['VMAJ']}.{$h['VMIN']}.{$h['VREV']}"; |
252 | |
253 | // Frame height/width |
254 | if ( !$h['FMBW'] || !$h['FMBH'] ) { |
255 | throw new OggException("Stream is undecodable because it has frame size of zero.", OGG_ERROR_UNDECODABLE); |
256 | } |
257 | $this->_frameWidth = $h['FMBW'] * 16; |
258 | $this->_frameHeight = $h['FMBH'] * 16; |
259 | |
260 | // Picture height/width |
261 | if ( $h['PICW'] > $this->_frameWidth || $h['PICH'] > $this->_frameHeight ) { |
262 | throw new OggException("Stream is undecodable because the picture width is greater than the frame width.", OGG_ERROR_UNDECODABLE); |
263 | } |
264 | $this->_pictureWidth = $h['PICW']; |
265 | $this->_pictureHeight = $h['PICH']; |
266 | |
267 | // Picture offset |
268 | $this->_offsetX = $h['PICX']; |
269 | $this->_offsetY = $h['PICY']; |
270 | // Frame rate |
271 | $this->_frameRate = $h['FRD'] == 0 ? 0 : $h['FRN'] / $h['FRD']; |
272 | // Physical aspect ratio |
273 | if ( !$h['PARN'] || !$h['PARD'] ) { |
274 | $this->_physicalAspectRatio = 1; |
275 | } else { |
276 | $this->_physicalAspectRatio = $h['PARN'] / $h['PARD']; |
277 | } |
278 | |
279 | // Color space |
280 | $colorSpaces = array( |
281 | 0 => 'Undefined', |
282 | 1 => 'Rec. 470M', |
283 | 2 => 'Rec. 470BG', |
284 | ); |
285 | if ( isset( $colorSpaces[$h['CS']] ) ) { |
286 | $this->_colorSpace = $colorSpaces[$h['CS']]; |
287 | } else { |
288 | $this->_colorSpace = 'Unknown (reserved)'; |
289 | } |
290 | |
291 | $this->_nomBitrate = $h['NOMBR']; |
292 | |
293 | $this->_quality = $h['QUAL']; |
294 | $this->_kfgShift = $h['KFGSHIFT']; |
295 | |
296 | $pixelFormats = array( |
297 | 0 => '4:2:0', |
298 | 1 => 'Unknown (reserved)', |
299 | 2 => '4:2:2', |
300 | 3 => '4:4:4', |
301 | ); |
302 | $this->_pixelFormat = $pixelFormats[$h['PF']]; |
303 | |
304 | switch ( $h['PF'] ) { |
305 | case 0: |
306 | $h['NSBS'] = |
307 | floor( ($h['FMBW'] + 1) / 2 ) * |
308 | floor( ($h['FMBH'] + 1) / 2 ) + 2 * |
309 | floor( ($h['FMBW'] + 3) / 4 ) * |
310 | floor( ($h['FMBH'] + 3) / 4 ); |
311 | $h['NBS'] = 6 * $h['FMBW'] * $h['FMBH']; |
312 | break; |
313 | case 2: |
314 | $h['NSBS'] = |
315 | floor( ($h['FMBW'] + 1) / 2 ) * |
316 | floor( ($h['FMBH'] + 1) / 2 ) + 2 * |
317 | floor( ($h['FMBW'] + 3) / 4 ) * |
318 | floor( ($h['FMBH'] + 1) / 2 ); |
319 | $h['NBS'] = 8 * $h['FMBW'] * $h['FMBH']; |
320 | break; |
321 | case 3: |
322 | $h['NSBS'] = |
323 | 3 * floor( ($h['FMBW'] + 1) / 2 ) * |
324 | floor( ($h['FMBH'] + 1) / 2 ); |
325 | $h['NBS'] = 12 * $h['FMBW'] * $h['FMBH']; |
326 | break; |
327 | default: |
328 | $h['NSBS'] = $h['NBS'] = 0; |
329 | } |
330 | $h['NMBS'] = $h['FMBW'] * $h['FMBH']; |
331 | |
332 | $this->_idHeader = $h; |
333 | } |
334 | |
335 | /** |
336 | * Get an associative array containing header information about the stream |
337 | * @access public |
338 | * @return array |
339 | */ |
340 | function getHeader() { |
341 | return $this->_idHeader; |
342 | } |
343 | |
344 | /** |
345 | * Get a short string describing the type of the stream |
346 | * @return string |
347 | */ |
348 | function getType() { |
349 | return 'Theora'; |
350 | } |
351 | |
352 | /** |
353 | * Decode the comments header |
354 | * @access private |
355 | */ |
356 | function _decodeCommentsHeader() |
357 | { |
358 | $this->_decodeCommonHeader(OGG_THEORA_COMMENTS_HEADER, OGG_THEORA_COMMENTS_PAGE_OFFSET); |
359 | $this->_decodeBareCommentsHeader(); |
360 | } |
361 | |
362 | } |
363 | ?> |