Add support for reading Ogg format

git-svn-id: http://php-reader.googlecode.com/svn/trunk@231 51a70ab9-7547-0410-9469-37e369ee0574
This commit is contained in:
svollbehr
2011-05-14 13:05:48 +00:00
parent ce0aa53e50
commit 87227f7758
3 changed files with 443 additions and 0 deletions

View File

@@ -0,0 +1,39 @@
<?php
/**
* Zend Framework
*
* LICENSE
*
* This source file is subject to the new BSD license that is bundled
* with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* http://framework.zend.com/license/new-bsd
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@zend.com so we can send you a copy immediately.
*
* @category Zend
* @package Zend_Media
* @subpackage Ogg
* @copyright Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
* @version $Id$
*/
/**#@+ @ignore */
require_once 'Zend/Media/Exception.php';
/**#@-*/
/**
* The Zend_Media_Ogg_Exception is thrown whenever an error occurs within the Ogg family of classes.
*
* @category Zend
* @package Zend_Media
* @subpackage Ogg
* @author Sven Vollbehr <sven@vollbehr.eu>
* @copyright Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
* @version $Id$
*/
class Zend_Media_Ogg_Exception extends Zend_Media_Exception
{}

235
src/Zend/Media/Ogg/Page.php Normal file
View File

@@ -0,0 +1,235 @@
<?php
/**
* Zend Framework
*
* LICENSE
*
* This source file is subject to the new BSD license that is bundled
* with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* http://framework.zend.com/license/new-bsd
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@zend.com so we can send you a copy immediately.
*
* @category Zend
* @package Zend_Media
* @subpackage Ogg
* @copyright Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
* @version $Id$
*/
/**
* This class represents an Ogg page. A physical bitstream consists of a sequence of Ogg pages containing data of one
* logical bitstream only. It usually contains a group of contiguous segments of one packet only, but sometimes packets
* are too large and need to be split over several pages.
*
* @category Zend
* @package Zend_Media
* @subpackage Ogg
* @author Sven Vollbehr <sven@vollbehr.eu>
* @copyright Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
* @version $Id$
*/
final class Zend_Media_Ogg_Page
{
/**
* The reader object.
*
* @var Zend_Io_Reader
*/
private $_reader;
/** @var string */
private $_capturePattern;
/** @var integer */
private $_streamStructureVersion;
/** @var integer */
private $_headerTypeFlag;
/** @var integer */
private $_granulePosition;
/** @var integer */
private $_bitstreamSerialNumber;
/** @var integer */
private $_pageSequenceNumber;
/** @var integer */
private $_crcChecksum;
/** @var integer */
private $_numberPageSegments;
/** @var Array */
private $_segmentTable = array();
/** @var integer */
private $_size;
/** @var integer */
private $_headerSize;
/** @var integer */
private $_pageSize;
/**
* Constructs the class with given parameters and reads object related data
* from the Ogg bitstream.
*
* @param Zend_Io_Reader $reader The reader object.
*/
public function __construct($reader)
{
$this->_reader = $reader;
$this->_capturePattern = $this->_reader->read(4);
if ($this->_capturePattern != 'OggS') {
require_once 'Zend/Media/Ogg/Exception.php';
throw new Zend_Media_Ogg_Exception('Not a valid Ogg bitstream');
}
$this->_streamStructureVersion = $this->_reader->readUInt8();
if ($this->_streamStructureVersion != 0) {
require_once 'Zend/Media/Ogg/Exception.php';
throw new Zend_Media_Ogg_Exception('Unsupported Ogg stream structure version');
}
$this->_headerTypeFlag = $this->_reader->readUInt8();
$this->_granulePosition = $this->_reader->readInt64LE();
$this->_bitstreamSerialNumber = $this->_reader->readUInt32LE();
$this->_pageSequenceNumber = $this->_reader->readUInt32LE();
$this->_crcChecksum = $this->_reader->readUInt32LE();
$this->_numberPageSegments = $this->_reader->readUInt8();
$this->_segmentTable = array();
for ($i = 0; $i < $this->_numberPageSegments; $i++) {
$this->_segmentTable[] = $this->_reader->readUInt8();
}
$this->_headerSize = $this->_numberPageSegments + 27;
$this->_pageSize = array_sum($this->_segmentTable);
$this->_size = $this->_headerSize + $this->_pageSize;
}
/**
* Returns this page's context identifier in the bitstream.
*
* @return integer
*/
public final function getHeaderTypeFlag()
{
return $this->_headerTypeFlag;
}
/**
* Returns total samples encoded after including all packets finished on this page (packets begun on this page but
* continuing on to the next page do not count).
*
* The rationale here is that the position specified in the frame header of the last page tells how long the data
* coded by the bitstream is. A truncated stream will still return the proper number of samples that can be decoded
* fully.
*
* A special value of '-1' (in two's complement) indicates that no packets finish on this page.
*
* @return integer
*/
public final function getGranulePosition()
{
return $this->_granulePosition;
}
/**
* Returns the logical bitstream serial number.
*
* Ogg allows for separate logical bitstreams to be mixed at page granularity in a physical bitstream. The most
* common case would be sequential arrangement, but it is possible to interleave pages for two separate bitstreams
* to be decoded concurrently. The serial number is the means by which pages physical pages are associated with a
* particular logical stream. Each logical stream must have a unique serial number within a physical stream.
*
* @return integer
*/
public final function getBitstreamSerialNumber()
{
return $this->_bitstreamSerialNumber;
}
/**
* Returns the page counter; lets us know if a page is lost (useful where packets span page boundaries).
*
* @return integer
*/
public final function getPageSequenceNumber()
{
return $this->_pageSequenceNumber;
}
/**
* Returns the 32 bit CRC value (direct algorithm, initial val and final XOR = 0, generator polynomial=0x04c11db7).
* The value is computed over the entire header (with the CRC field in the header set to zero) and then continued
* over the page. The CRC field is then filled with the computed value.
*
* @return integer
*/
public final function getCrcChecksum()
{
return $this->_crcChecksum;
}
/**
* Returns the number of segment entries to appear in the segment table. The maximum number of 255 segments (255
* bytes each) sets the maximum possible physical page size at 65307 bytes or just under 64kB (thus we know that a
* header corrupted so as destroy sizing/alignment information will not cause a runaway bitstream. We'll read in the
* page according to the corrupted size information that's guaranteed to be a reasonable size regardless, notice the
* checksum mismatch, drop sync and then look for recapture).
*
* @return integer
*/
public final function getNumberPageSegments()
{
return $this->_numberPageSegments;
}
/**
* Returns the lacing values for each packet segment physically appearing in this page are listed in contiguous
* order.
*
* @return integer
*/
public final function getSegmentTable()
{
return $this->_segmentTable;
}
/**
* Returns the total page size with the header in bytes.
*
* @return integer
*/
public final function getSize()
{
return $this->_size;
}
/**
* Returns the total header size in bytes.
*
* @return integer
*/
public final function getHeaderSize()
{
return $this->_headerSize;
}
/**
* Returns the total page size without the header in bytes. The page size is calculated directly from the known
* lacing values in the segment table.
*
* @return integer
*/
public final function getPageSize()
{
return $this->_pageSize;
}
}

View File

@@ -0,0 +1,169 @@
<?php
/**
* Zend Framework
*
* LICENSE
*
* This source file is subject to the new BSD license that is bundled
* with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* http://framework.zend.com/license/new-bsd
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@zend.com so we can send you a copy immediately.
*
* @category Zend
* @package Zend_Media
* @subpackage Ogg
* @copyright Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
* @version $Id$
*/
/**#@+ @ignore */
require_once 'Zend/Io/Reader.php';
require_once 'Zend/Io/FileReader.php';
require_once 'Zend/Media/Ogg/Page.php';
/**#@-*/
/**
* This class is a Zend_Io_Reader specialization that can read a file containing the Ogg bitstream format version 0 as
* described in {@link http://tools.ietf.org/html/rfc3533 RFC3533}. It is a general, freely-available encapsulation
* format for media streams. It is able to encapsulate any kind and number of video and audio encoding formats as well
* as other data streams in a single bitstream.
*
* @category Zend
* @package Zend_Media
* @subpackage Ogg
* @author Sven Vollbehr <sven@vollbehr.eu>
* @copyright Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
* @version $Id$
*/
final class Zend_Media_Ogg_Reader extends Zend_Io_Reader
{
/** @var Array */
private $_pages = array();
/** @var integer */
private $_currentPage = 0;
/** @var integer */
private $_currentPagePosition = 0;
/** @var integer */
private $_streamSize = 0;
/**
* Constructs the Ogg class with given file.
*
* @param string $filename The path to the file.
* @throws Zend_Io_Exception if an error occur in stream handling.
* @throws Zend_Media_Ogg_Exception if an error occurs in Ogg bitstream reading.
*/
public function __construct($filename)
{
$reader = new Zend_Io_FileReader($filename);
$fileSize = $reader->getSize();
while ($reader->getOffset() < $fileSize) {
$this->_pages[] = array(
'page' => $page = new Zend_Media_Ogg_Page($reader),
'offset' => $reader->getOffset()
);
$this->_streamSize += $page->getPageSize();
$reader->skip($page->getPageSize());
}
$reader->setOffset($this->_pages[$this->_currentPage]['offset']);
$this->_fd = $reader->getFileDescriptor();
}
/**
* Overwrite the method to return the current point of operation within the Ogg bitstream.
*
* @return integer
* @throws Zend_Io_Exception if an I/O error occurs
*/
public function getOffset()
{
$offset = 0;
for ($i = 0; $i < $this->_currentPage; $i++) {
$offset += $this->_pages[$i]['page']->getPageSize();
}
return $offset += $this->_currentPagePosition;
}
/**
* Overwrite the method to set the point of operation within the Ogg bitstream.
*
* @param integer $offset The new point of operation.
* @return void
* @throws Zend_Io_Exception if an I/O error occurs
*/
public function setOffset($offset)
{
$streamSize = 0;
for ($i = 0, $pageCount = count($this->_pages); $i < $pageCount; $i++) {
if (($streamSize + $this->_pages[$i]['page']->getPageSize()) >= $offset) {
$this->_currentPage = $i;
$this->_currentPagePosition = $offset - $streamSize;
parent::setOffset($this->_pages[$i]['offset'] + $this->_currentPagePosition);
break;
}
$streamSize += $this->_pages[$i]['page']->getPageSize();
}
}
/**
* Overwrite the method to return the Ogg bitstream size in bytes.
*
* @return integer
*/
public function getSize()
{
echo "getSize\n";
return $this->_streamSize;
}
/**
* Overwrite the method to jump <var>size</var> amount of bytes in the Ogg bitstream.
*
* @param integer $size The amount of bytes to jump within the Ogg bitstream.
* @return void
* @throws Zend_Io_Exception if an I/O error occurs
*/
public function skip($size)
{
$currentPageSize = $this->_pages[$this->_currentPage]['page']->getPageSize();
if (($this->_currentPagePosition + $size) >= $currentPageSize) {
parent::skip
(($currentPageSize - $this->_currentPagePosition) +
$this->_pages[++$this->_currentPage]['page']->getHeaderSize() +
($this->_currentPagePosition = ($size - $currentPageSize - $this->_currentPagePosition)));
} else {
$this->_currentPagePosition += $size;
parent::skip($size);
}
}
/**
* Overwrite the method to read bytes within the Ogg bitstream.
*
* @param integer $length The amount of bytes to read within the Ogg bitstream.
* @return string
* @throws Zend_Io_Exception if an I/O error occurs
*/
public function read($length)
{
$currentPageSize = $this->_pages[$this->_currentPage]['page']->getPageSize();
if (($this->_currentPagePosition + $length) >= $currentPageSize) {
$buffer = parent::read($currentPageSize - $this->_currentPagePosition);
parent::skip($this->_pages[++$this->_currentPage]['page']->getHeaderSize());
return $buffer . parent::read
($this->_currentPagePosition = ($length - ($currentPageSize - $this->_currentPagePosition)));
} else {
$buffer = parent::read($length);
$this->_currentPagePosition += $length;
return $buffer;
}
}
}