summaryrefslogtreecommitdiff
path: root/extensions/TimedMediaHandler/handlers/OggHandler/File_Ogg/File/Ogg/Theora.php
diff options
context:
space:
mode:
Diffstat (limited to 'extensions/TimedMediaHandler/handlers/OggHandler/File_Ogg/File/Ogg/Theora.php')
-rw-r--r--extensions/TimedMediaHandler/handlers/OggHandler/File_Ogg/File/Ogg/Theora.php240
1 files changed, 240 insertions, 0 deletions
diff --git a/extensions/TimedMediaHandler/handlers/OggHandler/File_Ogg/File/Ogg/Theora.php b/extensions/TimedMediaHandler/handlers/OggHandler/File_Ogg/File/Ogg/Theora.php
new file mode 100644
index 00000000..b801ea26
--- /dev/null
+++ b/extensions/TimedMediaHandler/handlers/OggHandler/File_Ogg/File/Ogg/Theora.php
@@ -0,0 +1,240 @@
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
+// +----------------------------------------------------------------------------+
+// | File_Ogg PEAR Package for Accessing Ogg Bitstreams |
+// | Copyright (c) 2005-2007 |
+// | David Grant <david@grant.org.uk> |
+// | Tim Starling <tstarling@wikimedia.org> |
+// +----------------------------------------------------------------------------+
+// | This library is free software; you can redistribute it and/or |
+// | modify it under the terms of the GNU Lesser General Public |
+// | License as published by the Free Software Foundation; either |
+// | version 2.1 of the License, or (at your option) any later version. |
+// | |
+// | This library is distributed in the hope that it will be useful, |
+// | but WITHOUT ANY WARRANTY; without even the implied warranty of |
+// | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
+// | Lesser General Public License for more details. |
+// | |
+// | You should have received a copy of the GNU Lesser General Public |
+// | License along with this library; if not, write to the Free Software |
+// | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
+// +----------------------------------------------------------------------------+
+
+
+define( 'OGG_THEORA_IDENTIFICATION_HEADER', 0x80 );
+define( 'OGG_THEORA_COMMENTS_HEADER', 0x81 );
+define( 'OGG_THEORA_IDENTIFICATION_PAGE_OFFSET', 0 );
+define( 'OGG_THEORA_COMMENTS_PAGE_OFFSET', 1 );
+
+
+/**
+ * @author David Grant <david@grant.org.uk>, Tim Starling <tstarling@wikimedia.org>
+ * @category File
+ * @copyright David Grant <david@grant.org.uk>, Tim Starling <tstarling@wikimedia.org>
+ * @license http://www.gnu.org/copyleft/lesser.html GNU LGPL
+ * @link http://pear.php.net/package/File_Ogg
+ * @link http://www.xiph.org/theora/
+ * @package File_Ogg
+ * @version CVS: $Id: Theora.php,v 1.9 2005/11/16 20:43:27 djg Exp $
+ */
+class File_Ogg_Theora extends File_Ogg_Media
+{
+ /**
+ * @access private
+ */
+ function __construct($streamSerial, $streamData, $filePointer)
+ {
+ parent::__construct($streamSerial, $streamData, $filePointer);
+ $this->_decodeIdentificationHeader();
+ $this->_decodeCommentsHeader();
+ $endSec = $this->getSecondsFromGranulePos( $this->_lastGranulePos );
+
+ $startSec = $this->getSecondsFromGranulePos( $this->_firstGranulePos );
+
+ //make sure the offset is worth taking into account oggz_chop related hack
+ if( $startSec > 1){
+ $this->_streamLength = $endSec - $startSec;
+ $this->_startOffset = $startSec;
+ }else{
+ $this->_streamLength = $endSec;
+ }
+
+ $this->_avgBitrate = $this->_streamLength ? ($this->_streamSize * 8) / $this->_streamLength : 0;
+ }
+ function getSecondsFromGranulePos($granulePos){
+ // Calculate GranulePos seconds
+ // First make some "numeric strings"
+ // These might not fit into PHP's integer type, but they will fit into
+ // the 53-bit mantissa of a double-precision number
+ $topWord = floatval( base_convert( substr( $granulePos, 0, 8 ), 16, 10 ) );
+ $bottomWord = floatval( base_convert( substr( $granulePos, 8, 8 ), 16, 10 ) );
+ // Calculate the keyframe position by shifting right by KFGSHIFT
+ // We don't use PHP's shift operators because they're terribly broken
+ // This is made slightly simpler by the fact that KFGSHIFT < 32
+ $keyFramePos = $topWord / pow(2, $this->_kfgShift - 32) +
+ floor( $bottomWord / pow(2, $this->_kfgShift) );
+ // Calculate the frame offset by masking off the top 64-KFGSHIFT bits
+ // This requires a bit of floating point trickery
+ $offset = fmod( $bottomWord, pow(2, $this->_kfgShift) );
+ // They didn't teach you that one at school did they?
+ // Now put it together with the frame rate to calculate time in seconds
+ return ( $keyFramePos + $offset ) / $this->_frameRate;
+ }
+ /**
+ * Get the 6-byte identification string expected in the common header
+ */
+ function getIdentificationString()
+ {
+ return OGG_STREAM_CAPTURE_THEORA;
+ }
+
+ /**
+ * Parse the identification header in a Theora stream.
+ * @access private
+ */
+ function _decodeIdentificationHeader()
+ {
+ $this->_decodeCommonHeader(OGG_THEORA_IDENTIFICATION_HEADER, OGG_THEORA_IDENTIFICATION_PAGE_OFFSET);
+ $h = File_Ogg::_readBigEndian( $this->_filePointer, array(
+ 'VMAJ' => 8,
+ 'VMIN' => 8,
+ 'VREV' => 8,
+ 'FMBW' => 16,
+ 'FMBH' => 16,
+ 'PICW' => 24,
+ 'PICH' => 24,
+ 'PICX' => 8,
+ 'PICY' => 8,
+ 'FRN' => 32,
+ 'FRD' => 32,
+ 'PARN' => 24,
+ 'PARD' => 24,
+ 'CS' => 8,
+ 'NOMBR' => 24,
+ 'QUAL' => 6,
+ 'KFGSHIFT' => 5,
+ 'PF' => 2));
+ if ( !$h ) {
+ throw new OggException("Stream is undecodable due to a truncated header.", OGG_ERROR_UNDECODABLE);
+ }
+
+ // Theora version
+ // Seems overly strict but this is what the spec says
+ // VREV is for backwards-compatible changes, apparently
+ if ( $h['VMAJ'] != 3 || $h['VMIN'] != 2 ) {
+ throw new OggException("Stream is undecodable due to an invalid theora version.", OGG_ERROR_UNDECODABLE);
+ }
+ $this->_theoraVersion = "{$h['VMAJ']}.{$h['VMIN']}.{$h['VREV']}";
+
+ // Frame height/width
+ if ( !$h['FMBW'] || !$h['FMBH'] ) {
+ throw new OggException("Stream is undecodable because it has frame size of zero.", OGG_ERROR_UNDECODABLE);
+ }
+ $this->_frameWidth = $h['FMBW'] * 16;
+ $this->_frameHeight = $h['FMBH'] * 16;
+
+ // Picture height/width
+ if ( $h['PICW'] > $this->_frameWidth || $h['PICH'] > $this->_frameHeight ) {
+ throw new OggException("Stream is undecodable because the picture width is greater than the frame width.", OGG_ERROR_UNDECODABLE);
+ }
+ $this->_pictureWidth = $h['PICW'];
+ $this->_pictureHeight = $h['PICH'];
+
+ // Picture offset
+ $this->_offsetX = $h['PICX'];
+ $this->_offsetY = $h['PICY'];
+ // Frame rate
+ $this->_frameRate = $h['FRD'] == 0 ? 0 : $h['FRN'] / $h['FRD'];
+ // Physical aspect ratio
+ if ( !$h['PARN'] || !$h['PARD'] ) {
+ $this->_physicalAspectRatio = 1;
+ } else {
+ $this->_physicalAspectRatio = $h['PARN'] / $h['PARD'];
+ }
+
+ // Color space
+ $colorSpaces = array(
+ 0 => 'Undefined',
+ 1 => 'Rec. 470M',
+ 2 => 'Rec. 470BG',
+ );
+ if ( isset( $colorSpaces[$h['CS']] ) ) {
+ $this->_colorSpace = $colorSpaces[$h['CS']];
+ } else {
+ $this->_colorSpace = 'Unknown (reserved)';
+ }
+
+ $this->_nomBitrate = $h['NOMBR'];
+
+ $this->_quality = $h['QUAL'];
+ $this->_kfgShift = $h['KFGSHIFT'];
+
+ $pixelFormats = array(
+ 0 => '4:2:0',
+ 1 => 'Unknown (reserved)',
+ 2 => '4:2:2',
+ 3 => '4:4:4',
+ );
+ $this->_pixelFormat = $pixelFormats[$h['PF']];
+
+ switch ( $h['PF'] ) {
+ case 0:
+ $h['NSBS'] =
+ floor( ($h['FMBW'] + 1) / 2 ) *
+ floor( ($h['FMBH'] + 1) / 2 ) + 2 *
+ floor( ($h['FMBW'] + 3) / 4 ) *
+ floor( ($h['FMBH'] + 3) / 4 );
+ $h['NBS'] = 6 * $h['FMBW'] * $h['FMBH'];
+ break;
+ case 2:
+ $h['NSBS'] =
+ floor( ($h['FMBW'] + 1) / 2 ) *
+ floor( ($h['FMBH'] + 1) / 2 ) + 2 *
+ floor( ($h['FMBW'] + 3) / 4 ) *
+ floor( ($h['FMBH'] + 1) / 2 );
+ $h['NBS'] = 8 * $h['FMBW'] * $h['FMBH'];
+ break;
+ case 3:
+ $h['NSBS'] =
+ 3 * floor( ($h['FMBW'] + 1) / 2 ) *
+ floor( ($h['FMBH'] + 1) / 2 );
+ $h['NBS'] = 12 * $h['FMBW'] * $h['FMBH'];
+ break;
+ default:
+ $h['NSBS'] = $h['NBS'] = 0;
+ }
+ $h['NMBS'] = $h['FMBW'] * $h['FMBH'];
+
+ $this->_idHeader = $h;
+ }
+
+ /**
+ * Get an associative array containing header information about the stream
+ * @access public
+ * @return array
+ */
+ function getHeader() {
+ return $this->_idHeader;
+ }
+
+ /**
+ * Get a short string describing the type of the stream
+ * @return string
+ */
+ function getType() {
+ return 'Theora';
+ }
+
+ /**
+ * Decode the comments header
+ * @access private
+ */
+ function _decodeCommentsHeader()
+ {
+ $this->_decodeCommonHeader(OGG_THEORA_COMMENTS_HEADER, OGG_THEORA_COMMENTS_PAGE_OFFSET);
+ $this->_decodeBareCommentsHeader();
+ }
+
+}
+?>