Skip to content

Commit

Permalink
Add Opus Support
Browse files Browse the repository at this point in the history
  • Loading branch information
bit committed Sep 27, 2013
1 parent 98ee703 commit 13107d4
Show file tree
Hide file tree
Showing 4 changed files with 157 additions and 17 deletions.
13 changes: 13 additions & 0 deletions File/Ogg.php
Expand Up @@ -47,6 +47,10 @@
* @access public
*/
define("OGG_STREAM_FLAC", 4);
/**
* @access public
*/
define("OGG_STREAM_OPUS", 5);

/**
* Capture pattern to determine if a file is an Ogg physical stream.
Expand Down Expand Up @@ -85,6 +89,11 @@
* @access private
*/
define("OGG_STREAM_CAPTURE_THEORA", "theora");
/**
* Capture pattern for an Ogg Opus logical stream.
* @access private
*/
define("OGG_STREAM_CAPTURE_OPUS", "OpusHead");
/**
* Error thrown if the file location passed is nonexistant or unreadable.
*
Expand Down Expand Up @@ -118,6 +127,7 @@
require_once("File/Ogg/Speex.php");
require_once("File/Ogg/Theora.php");
require_once("File/Ogg/Vorbis.php");
require_once("File/Ogg/Opus.php");


/**
Expand Down Expand Up @@ -477,6 +487,9 @@ function _splitStreams()
} elseif (preg_match("/" . OGG_STREAM_CAPTURE_THEORA . "/", $pattern)) {
$this->_streamList[$stream_serial]['stream_type'] = OGG_STREAM_THEORA;
$stream = new File_Ogg_Theora($stream_serial, $streamData, $this->_filePointer);
} elseif (preg_match("/" . OGG_STREAM_CAPTURE_OPUS . "/", $pattern)) {
$this->_streamList[$stream_serial]['stream_type'] = OGG_STREAM_OPUS;
$stream = new File_Ogg_Opus($stream_serial, $streamData, $this->_filePointer);
} else {
$streamData['stream_type'] = "unknown";
$stream = false;
Expand Down
17 changes: 17 additions & 0 deletions File/Ogg/Media.php
Expand Up @@ -135,6 +135,23 @@ function _decodeBareCommentsHeader()
}
}

/**
* Number of channels used in this stream
*
* This function returns the number of channels used in this stream. This
* can range from 1 to 255, but will likely be 2 (stereo) or 1 (mono).
*
* @access public
* @return int
* @see File_Ogg_Vorbis::isMono()
* @see File_Ogg_Vorbis::isStereo()
* @see File_Ogg_Vorbis::isQuadrophonic()
*/
function getChannels()
{
return ($this->_channels);
}

/**
* Provides a list of the comments extracted from the Vorbis stream.
*
Expand Down
127 changes: 127 additions & 0 deletions File/Ogg/Opus.php
@@ -0,0 +1,127 @@
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4: */
// +----------------------------------------------------------------------------+
// | File_Ogg PEAR Package for Accessing Ogg Bitstreams |
// | Copyright (c) 2013 |
// | Jan Gerber <jgerber@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 |
// +----------------------------------------------------------------------------+

require_once('File/Ogg/Media.php');

define( 'OGG_OPUS_COMMENTS_PAGE_OFFSET', 1 );

/**
* @author Jan Gerber <jgerber@wikimedia.org>
* @category File
* @copyright Jan Gerber <jgerber@wikimedia.org>
* @license http://www.gnu.org/copyleft/lesser.html GNU LGPL
* @link http://pear.php.net/package/File_Ogg
* @link http://www.opus-codec.org/
* @package File_Ogg
* @version 1
*/
class File_Ogg_Opus extends File_Ogg_Media
{
/**
* @access private
*/
function __construct($streamSerial, $streamData, $filePointer)
{
parent::__construct($streamSerial, $streamData, $filePointer);
$this->_decodeHeader();
$this->_decodeCommentsHeader();

$endSec = $this->getSecondsFromGranulePos( $this->_lastGranulePos );
$startSec = $this->getSecondsFromGranulePos( $this->_firstGranulePos );

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 ){
return (( '0x' . substr( $granulePos, 0, 8 ) ) * pow(2, 32)
+ ( '0x' . substr( $granulePos, 8, 8 ) )
- $this->_header['pre_skip'])
/ 48000;
}

/**
* Get a short string describing the type of the stream
* @return string
*/
function getType()
{
return 'Opus';
}

/**
* Decode the stream header
* @access private
*/
function _decodeHeader()
{
fseek($this->_filePointer, $this->_streamData['pages'][0]['body_offset'], SEEK_SET);
// The first 8 characters should be "OpusHead".
if (fread($this->_filePointer, 8) != 'OpusHead')
throw new PEAR_Exception("Stream is undecodable due to a malformed header.", OGG_ERROR_UNDECODABLE);

$this->_header = File_Ogg::_readLittleEndian($this->_filePointer, array(
'opus_version' => 8,
'nb_channels' => 8,
'pre_skip' => 16,
'audio_sample_rate' => 32,
'output_gain' => 16,
'channel_mapping_family'=> 8,
));
$this->_channels = $this->_header['nb_channels'];
}

/**
* Get an associative array containing header information about the stream
* @access public
* @return array
*/
function getHeader() {
return $this->_header;
}

function getSampleRate()
{
//Opus always outputs 48kHz, the header only lists
//the samplerate of the source as reference
return 48000;
}

/**
* Decode the comments header
* @access private
*/
function _decodeCommentsHeader()
{
$id = 'OpusTags';
$this->_decodeCommonHeader(false, OGG_OPUS_COMMENTS_PAGE_OFFSET);
if(fread($this->_filePointer, strlen($id)) !== $id)
throw new PEAR_Exception("Stream is undecodable due to a malformed header.", OGG_ERROR_UNDECODABLE);
$this->_decodeBareCommentsHeader();
}
}
?>
17 changes: 0 additions & 17 deletions File/Ogg/Vorbis.php
Expand Up @@ -313,23 +313,6 @@ function getEncoderVersion()
return ($this->_version);
}

/**
* Number of channels used in this stream
*
* This function returns the number of channels used in this stream. This
* can range from 1 to 255, but will likely be 2 (stereo) or 1 (mono).
*
* @access public
* @return int
* @see File_Ogg_Vorbis::isMono()
* @see File_Ogg_Vorbis::isStereo()
* @see File_Ogg_Vorbis::isQuadrophonic()
*/
function getChannels()
{
return ($this->_channels);
}

/**
* Samples per second.
*
Expand Down

0 comments on commit 13107d4

Please sign in to comment.