Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 108
0.00% covered (danger)
0.00%
0 / 7
CRAP
0.00% covered (danger)
0.00%
0 / 1
File_Ogg_Theora
0.00% covered (danger)
0.00%
0 / 104
0.00% covered (danger)
0.00%
0 / 7
600
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 10
0.00% covered (danger)
0.00%
0 / 1
12
 getSecondsFromGranulePos
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
2
 getIdentificationString
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 _decodeIdentificationHeader
0.00% covered (danger)
0.00%
0 / 83
0.00% covered (danger)
0.00%
0 / 1
272
 getHeader
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getType
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 _decodeCommentsHeader
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
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
24use MediaWiki\TimedMediaHandler\Handlers\OggHandler\OggException;
25
26define( 'OGG_THEORA_IDENTIFICATION_HEADER', 0x80 );
27define( 'OGG_THEORA_COMMENTS_HEADER', 0x81 );
28define( 'OGG_THEORA_IDENTIFICATION_PAGE_OFFSET', 0 );
29define( '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 */
42class 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?>