Вход Регистрация
Файл: moduls/PEAR/Ogg.php
Строк: 530
<?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 |
// +----------------------------------------------------------------------------+

/**
 * @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
 * @package     File_Ogg
 * @version     CVS: $Id: Ogg.php,v 1.15 2008/01/31 04:41:44 tstarling Exp $
 */

/**
 * @access  public
 */
define("OGG_STREAM_VORBIS",     1);
/**
 * @access  public
 */
define("OGG_STREAM_THEORA",     2);
/**
 * @access  public
 */
define("OGG_STREAM_SPEEX",      3);
/**
 * @access  public
 */
define("OGG_STREAM_FLAC",       4);

/**
 * Capture pattern to determine if a file is an Ogg physical stream.
 * 
 * @access  private
 */
define("OGG_CAPTURE_PATTERN""OggS");
/**
 * Maximum size of an Ogg stream page plus four.  This value is specified to allow
 * efficient parsing of the physical stream.  The extra four is a paranoid measure
 * to make sure a capture pattern is not split into two parts accidentally.
 * 
 * @access  private
 */
define("OGG_MAXIMUM_PAGE_SIZE"65311);
/**
 * Capture pattern for an Ogg Vorbis logical stream.
 * 
 * @access  private
 */
define("OGG_STREAM_CAPTURE_VORBIS""vorbis");
/**
 * Capture pattern for an Ogg Speex logical stream.
 * @access  private
 */
define("OGG_STREAM_CAPTURE_SPEEX",  "Speex   ");
/**
 * Capture pattern for an Ogg FLAC logical stream.
 * 
 * @access  private
 */
define("OGG_STREAM_CAPTURE_FLAC",   "FLAC");
/**
 * Capture pattern for an Ogg Theora logical stream.
 * 
 * @access  private
 */
define("OGG_STREAM_CAPTURE_THEORA""theora");
/**
 * Error thrown if the file location passed is nonexistant or unreadable.
 * 
 * @access  private
 */
define("OGG_ERROR_INVALID_FILE"1);
/**
 * Error thrown if the user attempts to extract an unsupported logical stream.
 * 
 * @access  private
 */
define("OGG_ERROR_UNSUPPORTED",  2);
/**
 * Error thrown if the user attempts to extract an logical stream with no
 * corresponding serial number.
 * 
 * @access  private
 */
define("OGG_ERROR_BAD_SERIAL",   3);
/**
 * Error thrown if the stream appears to be corrupted.
 * 
 * @access  private
 */
define("OGG_ERROR_UNDECODABLE",      4);

require_once(
'PEAR.php');
require_once(
'Exception.php');
require_once(
'Ogg/Bitstream.php');
require_once(
"Ogg/Flac.php");
require_once(
"Ogg/Speex.php");
require_once(
"Ogg/Theora.php");
require_once(
"Ogg/Vorbis.php");


/**
 * Class for parsing a ogg bitstream.
 *
 * This class provides a means to access several types of logical bitstreams (e.g. Vorbis)
 * within a Ogg physical bitstream.
 *
 * @link    http://www.xiph.org/ogg/doc/
 * @package File_Ogg
 */
class File_Ogg
{
    
/**
     * File pointer to Ogg container.
     *
     * This is the file pointer used for extracting data from the Ogg stream.  It is
     * the result of a standard fopen call.
     *
     * @var     pointer
     * @access  private
     */
    
var $_filePointer;

    
/**
     * The container for all logical streams.
     *
     * List of all of the unique streams in the Ogg physical stream.  The key
     * used is the unique serial number assigned to the logical stream by the
     * encoding application.
     *
     * @var     array
     * @access  private
     */
    
var $_streamList = array();
    var 
$_streams = array();

    
/**
     * Length in seconds of each stream group
     */
    
var $_groupLengths = array();

    
/**
     * Total length in seconds of the entire file
     */
    
var $_totalLength;

    
/**
     * Returns an interface to an Ogg physical stream.
     *
     * This method takes the path to a local file and examines it for a physical
     * ogg bitsream.  After instantiation, the user should query the object for
     * the logical bitstreams held within the ogg container.
     *
     * @access  public
     * @param   string  $fileLocation   The path of the file to be examined.
     */
    
function File_Ogg($fileLocation)
    {
        
clearstatcache();
        if (! 
file_exists($fileLocation)) {
            throw new 
PEAR_Exception("Couldn't Open File.  Check File Path."OGG_ERROR_INVALID_FILE);
        }

        
// Open this file as a binary, and split the file into streams.
        
$this->_filePointer fopen($fileLocation"rb");
        if (!
is_resource($this->_filePointer))
            throw new 
PEAR_Exception("Couldn't Open File.  Check File Permissions."OGG_ERROR_INVALID_FILE);

        
// Check for a stream at the start
        
$magic fread($this->_filePointerstrlen(OGG_CAPTURE_PATTERN));
        if (
$magic !== OGG_CAPTURE_PATTERN) {
            throw new 
PEAR_Exception("Couldn't read file: Incorrect magic number."OGG_ERROR_UNDECODABLE);
        }
        
fseek($this->_filePointer0SEEK_SET);

        
$this->_splitStreams();
        
fclose($this->_filePointer);
    }

    
/**
     * Little-endian equivalent for bin2hex
     * @static
     */
    
static function _littleEndianBin2Hex$bin ) {
        
$bigEndian bin2hex$bin );
        
// Reverse entire string
        
$reversed strrev$bigEndian );
        
// Swap nibbles back
        
for ( $i 0$j strlen $bigEndian ); $i $j$i += ) {
            
$temp $reversed[$i];
            
$reversed[$i] = $reversed[$i+1];
            
$reversed[$i+1] = $temp;
        }
        return 
$reversed;
    }


    
/**
     * Read a binary structure from a file. An array of unsigned integers are read.
     * Large integers are upgraded to floating point on overflow.
     *
     * Format is big-endian as per Theora bit packing convention, this function 
     * won't work for Vorbis.
     *
     * @param   resource    $file
     * @param   array       $fields Associative array mapping name to length in bits
     */
    
static function _readBigEndian($file$fields
    {
        
$bufferLength ceil(array_sum($fields) / 8);
        
$buffer fread($file$bufferLength);
        if (
strlen($buffer) != $bufferLength) {
            throw new 
PEAR_Exception('Unexpected end of file'OGG_ERROR_UNDECODABLE);
        }
        
$bytePos 0;
        
$bitPos 0;
        
$byteValue ord($buffer[0]);
        
$output = array();
        foreach (
$fields as $name => $width) {
            if (
$width == && $bitPos == 0) {
                
// Byte aligned case
                
$bytes $width 8;
                
$endBytePos $bytePos $bytes;
                
$value 0;
                while (
$bytePos $endBytePos) {
                    
$value = ($value 256) + ord($buffer[$bytePos]);
                    
$bytePos++;
                }
                if (
$bytePos strlen($buffer)) {
                    
$byteValue ord($buffer[$bytePos]);
                }
            } else {
                
// General case
                
$bitsRemaining $width;
                
$value 0;
                while (
$bitsRemaining 0) {
                    
$bitsToRead min($bitsRemaining$bitPos);
                    
$byteValue <<= $bitsToRead;
                    
$overflow = ($byteValue 0xff00) >> 8;
                    
$byteValue &= $byteValue 0xff;

                    
$bitPos += $bitsToRead;
                    
$bitsRemaining -= $bitsToRead;
                    
$value += $overflow pow(2$bitsRemaining);

                    if (
$bitPos >= 8) {
                        
$bitPos 0;
                        
$bytePos++;
                        if (
$bitsRemaining <= 0) {
                            break;
                        }
                        
$byteValue ord($buffer[$bytePos]);
                    }
                }
            }
            
$output[$name] = $value;
            
assert($bytePos <= $bufferLength);
        }
        return 
$output;
    }

    
/**
     * Read a binary structure from a file. An array of unsigned integers are read.
     * Large integers are upgraded to floating point on overflow.
     *
     * Format is little-endian as per Vorbis bit packing convention.
     *
     * @param   resource    $file
     * @param   array       $fields Associative array mapping name to length in bits
     */
    
static function _readLittleEndian$file$fields ) {
        
$bufferLength ceil(array_sum($fields) / 8);
        
$buffer fread($file$bufferLength);
        if (
strlen($buffer) != $bufferLength) {
            throw new 
PEAR_Exception('Unexpected end of file'OGG_ERROR_UNDECODABLE);
        }

        
$bytePos 0;
        
$bitPos 0;
        
$byteValue ord($buffer[0]) << 8;
        
$output = array();
        foreach (
$fields as $name => $width) {
            if (
$width == && $bitPos == 0) {
                
// Byte aligned case
                
$bytes $width 8;
                
$value 0;
                for (
$i 0$i $bytes$i++, $bytePos++) {
                    
$value += pow(256$i) * ord($buffer[$bytePos]);
                }
                if (
$bytePos strlen($buffer)) {
                    
$byteValue ord($buffer[$bytePos]) << 8;
                }
            } else {
                
// General case
                
$bitsRemaining $width;
                
$value 0;
                while (
$bitsRemaining 0) {
                    
$bitsToRead min($bitsRemaining$bitPos);
                    
$byteValue >>= $bitsToRead;
                    
$overflow = ($byteValue 0xff) >> ($bitsToRead);
                    
$byteValue &= 0xff00;

                    
$value += $overflow pow(2$width $bitsRemaining);
                    
$bitPos += $bitsToRead;
                    
$bitsRemaining -= $bitsToRead;

                    if (
$bitPos >= 8) {
                        
$bitPos 0;
                        
$bytePos++;
                        if (
$bitsRemaining <= 0) {
                            break;
                        }
                        
$byteValue ord($buffer[$bytePos]) << 8;
                    }
                }
            }
            
$output[$name] = $value;
            
assert($bytePos <= $bufferLength);
        }
        return 
$output;
    }


    
/**
     * @access  private
     */
    
function _decodePageHeader($pageData$pageOffset$groupId)
    {
        
// Extract the various bits and pieces found in each packet header.
        
if (substr($pageData04) != OGG_CAPTURE_PATTERN)
            return (
false);

        
$stream_version unpack("C1data"substr($pageData41));
        if (
$stream_version['data'] != 0x00)
            return (
false);

        
$header_flag     unpack("Cdata"substr($pageData51));

        
// Exact granule position
        
$abs_granule_pos self::_littleEndianBin2Hexsubstr($pageData68));
        
// Approximate (floating point) granule position
        
$pos unpack("Va/Vb"substr($pageData68));
        
$approx_granule_pos $pos['a'] + $pos['b'] * pow(232);

        
// Serial number for the current datastream.
        
$stream_serial   unpack("Vdata"substr($pageData144));
        
$page_sequence   unpack("Vdata"substr($pageData184));
        
$checksum        unpack("Vdata"substr($pageData224));
        
$page_segments   unpack("Cdata"substr($pageData261));
        
$segments_total  0;
        for (
$i 0$i $page_segments['data']; ++$i) {
            
$segment_length unpack("Cdata"substr($pageData26 + ($i 1), 1));
            
$segments_total += $segment_length['data'];
        }
        
$pageFinish $pageOffset 27 $page_segments['data'] + $segments_total;
        
$page = array(
            
'stream_version'        => $stream_version['data'],
            
'header_flag'           => $header_flag['data'],
            
'abs_granule_pos'       => $abs_granule_pos,
            
'approx_granule_pos'    => $approx_granule_pos,
            
'checksum'              => sprintf("%u"$checksum['data']),
            
'segments'              => $page_segments['data'],
            
'head_offset'           => $pageOffset,
            
'body_offset'           => $pageOffset 27 $page_segments['data'],
            
'body_finish'           => $pageFinish,
            
'data_length'           => $pageFinish $pageOffset,
            
'group'                 => $groupId,
        );

        
$this->_streamList[$stream_serial['data']]['stream_page'][$page_sequence['data']] = $page;
        return 
$page;
    }
    
    
/**
     *  @access         private
     */
    
function _splitStreams()
    {
        
// Loop through the physical stream until there are no more pages to read.
        
$groupId 0;
        
$openStreams 0;
        
$this_page_offset 0;
        while (!
feof($this->_filePointer)) {
            
$pageData fread($this->_filePointer282);
            if (
strval($pageData) === '') {
                break;
            }
            
$page $this->_decodePageHeader($pageData$this_page_offset$groupId);
            if (
$page === false) {
                throw new 
PEAR_Exception("Cannot decode Ogg file: Invalid page at offset $this_page_offset"OGG_ERROR_UNDECODABLE);
            }

            
// Keep track of multiplexed groups
            
if ($page['header_flag'] & 2/*bos*/) {
                
$openStreams++;
            } elseif (
$page['header_flag'] & 4/*eos*/) {
                
$openStreams--;
                if (!
$openStreams) {
                    
// End of group
                    
$groupId++;
                }
            }
            if (
$openStreams 0) {
                throw new 
PEAR_Exception("Unexpected end of stream"OGG_ERROR_UNDECODABLE);
            }

            
$this_page_offset $page['body_finish'];
            
fseek($this->_filePointer$this_page_offsetSEEK_SET);
        }
        
// Loop through the streams, and find out what type of stream is available.
        
$groupLengths = array();
        foreach (
$this->_streamList as $stream_serial => $pages) {
            
fseek($this->_filePointer$pages['stream_page'][0]['body_offset'], SEEK_SET);
            
$pattern fread($this->_filePointer8);
            if (
preg_match("/" OGG_STREAM_CAPTURE_VORBIS "/"$pattern)) {
                
$this->_streamList[$stream_serial]['stream_type'] = OGG_STREAM_VORBIS;
                
$stream = new File_Ogg_Vorbis($stream_serial$pages['stream_page'], $this->_filePointer);
            } elseif (
preg_match("/" OGG_STREAM_CAPTURE_SPEEX "/"$pattern)) {
                
$this->_streamList[$stream_serial]['stream_type'] = OGG_STREAM_SPEEX;
                
$stream = new File_Ogg_Speex($stream_serial$pages['stream_page'], $this->_filePointer);
            } elseif (
preg_match("/" OGG_STREAM_CAPTURE_FLAC "/"$pattern)) {
                
$this->_streamList[$stream_serial]['stream_type'] = OGG_STREAM_FLAC;
                
$stream = new File_Ogg_Flac($stream_serial$pages['stream_page'], $this->_filePointer);
            } elseif (
preg_match("/" OGG_STREAM_CAPTURE_THEORA "/"$pattern)) {
                
$this->_streamList[$stream_serial]['stream_type'] = OGG_STREAM_THEORA;
                
$stream = new File_Ogg_Theora($stream_serial$pages['stream_page'], $this->_filePointer);
            } else {
                
$pages['stream_type'] = "unknown";
                
$stream false;
            }

            if (
$stream) {
                
$this->_streams[$stream_serial] = $stream;
                
$group $pages['stream_page'][0]['group'];
                if (isset(
$groupLengths[$group])) {
                    
$groupLengths[$group] = max($groupLengths[$group], $stream->getLength());
                } else {
                    
$groupLengths[$group] = $stream->getLength();
                }
            }
        }
        
$this->_groupLengths $groupLengths;
        
$this->_totalLength array_sum$groupLengths );
        unset(
$this->_streamList);
    }
    
    
/**
     * Returns the overead percentage used by the Ogg headers.
     * 
     * This function returns the percentage of the total stream size
     * used for Ogg headers.
     *
     * @return float
     */
    
function getOverhead() {
        
$header_size    0;
        
$stream_size    0;
        foreach (
$this->_streams as $serial => $stream) {
            foreach (
$stream->_streamList as $offset => $stream_data) {
                
$header_size += $stream_data['body_offset'] - $stream_data['head_offset'];
                
$stream_size  $stream_data['body_finish'];
            }
        }
        return 
sprintf("%0.2f", ($header_size $stream_size) * 100);
    }
    
    
/**
     * Returns the appropriate logical bitstream that corresponds to the provided serial.
     *
     * This function returns a logical bitstream contained within the Ogg physical
     * stream, corresponding to the serial used as the offset for that bitstream.
     * The returned stream may be Vorbis, Speex, FLAC or Theora, although the only
     * usable bitstream is Vorbis.
     *
     * @return File_Ogg_Bitstream
     */
    
function &getStream($streamSerial)
    {
        if (! 
array_key_exists($streamSerial$this->_streams))
                throw new 
PEAR_Exception("The stream number is invalid."OGG_ERROR_BAD_SERIAL);

        return 
$this->_streams[$streamSerial];
    }
    
    
/**
     * This function returns true if a logical bitstream of the requested type can be found.
     *
     * This function checks the contents of this ogg physical bitstream for of logical
     * bitstream corresponding to the supplied type.  If one is found, the function returns
     * true, otherwise it return false.
     *
     * @param   int     $streamType
     * @return  boolean
     */
    
function hasStream($streamType)
    {
        foreach (
$this->_stream as $stream) {
            if (
$stream['stream_type'] == $streamType)
                return (
true);
        }
        return (
false);
    }
    
    
/**
     * Returns an array of logical streams inside this physical bitstream.
     *
     * This function returns an array of logical streams found within this physical
     * bitstream.  If a filter is provided, only logical streams of the requested type
     * are returned, as an array of serial numbers.  If no filter is provided, this
     * function returns a two-dimensional array, with the stream type as the primary key,
     * and a value consisting of an array of stream serial numbers.
     *
     * @param  int      $filter
     * @return array
     */
    
function listStreams($filter null)
    {
        
$streams = array();
        
// Loops through the streams and assign them to an appropriate index,
        // ready for filtering the second part of this function.
        
foreach ($this->_streams as $serial => $stream) {
            
$stream_type 0;
            switch (
get_class($stream)) {
                case 
"file_ogg_flac":
                    
$stream_type OGG_STREAM_FLAC;
                    break;
                case 
"file_ogg_speex":
                    
$stream_type OGG_STREAM_SPEEX;
                    break;
                case 
"file_ogg_theora":
                    
$stream_type OGG_STREAM_THEORA;
                    break;
                case 
"file_ogg_vorbis":
                    
$stream_type OGG_STREAM_VORBIS;
                    break;
            }
            if (! isset(
$streams[$stream_type]))
                
// Initialise the result list for this stream type.
                
$streams[$stream_type] = array();
                        
            
$streams[$stream_type][] = $serial;
        }

        
// Perform filtering.
        
if (is_null($filter))
            return (
$streams);
        elseif (isset(
$streams[$filter]))
            return (
$streams[$filter]);
        else
            return array();
    }

    
/**
     * Get the total length of the group of streams
     */
    
function getLength() {
        return 
$this->_totalLength;
    }
}
?>
Онлайн: 2
Реклама