diff --git a/src/MPEG/Audio.php b/src/MPEG/Audio.php index 05e4ac8..7c92889 100644 --- a/src/MPEG/Audio.php +++ b/src/MPEG/Audio.php @@ -271,7 +271,7 @@ final class MPEG_Audio extends MPEG_Audio_Object */ public function getFormattedLengthEstimate() { - return $this->_formatTime($this->getLengthEstimate()); + return $this->formatTime($this->getLengthEstimate()); } /** @@ -285,28 +285,7 @@ final class MPEG_Audio extends MPEG_Audio_Object */ public function getFormattedLength() { - return $this->_formatTime($this->getLength()); - } - - /** - * Formats given time in seconds into the form of - * [hours]:minutes:seconds.milliseconds. - * - * @param integer $seconds The time to format, in seconds - * @return string - */ - private function _formatTime($seconds) - { - $milliseconds = round(($seconds - floor($seconds)) * 1000); - $seconds = floor($seconds); - $minutes = floor($seconds / 60); - $hours = floor($minutes / 60); - return - ($minutes > 0 ? - ($hours > 0 ? $hours . ":" . - str_pad($minutes % 60, 2, "0", STR_PAD_LEFT) : $minutes % 60) . ":" . - str_pad($seconds % 60, 2, "0", STR_PAD_LEFT) : $seconds % 60) . "." . - str_pad($milliseconds, 3, "0", STR_PAD_LEFT); + return $this->formatTime($this->getLength()); } /** diff --git a/src/MPEG/Object.php b/src/MPEG/Object.php index 1e4889c..86968ce 100644 --- a/src/MPEG/Object.php +++ b/src/MPEG/Object.php @@ -195,6 +195,27 @@ abstract class MPEG_Object return 0; } + /** + * Formats given time in seconds into the form of + * [hours]:minutes:seconds.milliseconds. + * + * @param integer $seconds The time to format, in seconds + * @return string + */ + protected function formatTime($seconds) + { + $milliseconds = round(($seconds - floor($seconds)) * 1000); + $seconds = floor($seconds); + $minutes = floor($seconds / 60); + $hours = floor($minutes / 60); + return + ($minutes > 0 ? + ($hours > 0 ? $hours . ":" . + str_pad($minutes % 60, 2, "0", STR_PAD_LEFT) : $minutes % 60) . ":" . + str_pad($seconds % 60, 2, "0", STR_PAD_LEFT) : $seconds % 60) . "." . + str_pad($milliseconds, 3, "0", STR_PAD_LEFT); + } + /** * Magic function so that $obj->value will work. * diff --git a/src/MPEG/ProgramStream.php b/src/MPEG/ProgramStream.php new file mode 100644 index 0000000..453d8b6 --- /dev/null +++ b/src/MPEG/ProgramStream.php @@ -0,0 +1,145 @@ + + * @copyright Copyright (c) 2008 The PHP Reader Project Workgroup + * @license http://code.google.com/p/php-reader/wiki/License New BSD License + * @version $Rev: 1 $ + * @todo Full implementation + */ +final class MPEG_ProgramStream extends MPEG_Object +{ + /** @var integer */ + private $_length; + + /** + * Constructs the MPEG_ProgramStream class with given file and options. + * + * @param string|Reader $filename The path to the file, file descriptor of an + * opened file, or {@link Reader} instance. + * @param Array $options The options array. + */ + public function __construct($filename, $options = array()) + { + if ($filename instanceof Reader) + $reader = &$filename; + else + $reader = new Reader($filename); + + parent::__construct($reader, $options); + + $startCode = 0; $startTime = 0; + $pictureCount = 0; $pictureRate = 0; + $rates = array ( 0, 23.976, 24, 25, 29.97, 30, 50, 59.94, 60 ); + $foundSeqHdr = false; $foundGOP = false; + + do { + do { + $startCode = $this->nextStartCode(); + } while ($startCode != 0x1b3 && $startCode != 0x1b8); + if ($startCode == 0x1b3 /* sequence_header_code */ && $pictureRate == 0) { + $i1 = $this->_reader->readUInt32BE(); + $i2 = $this->_reader->readUInt32BE(); + if (!Twiddling::testAllBits($i2, 0x2000)) + throw new RuntimeException("Invalid mark"); + $pictureRate = $rates[Twiddling::getValue($i1, 4, 8)]; + $foundSeqHdr = true; + } + if ($startCode == 0x1b8 /* group_start_code */) { + $tmp = $this->_reader->readUInt32BE(); + $startTime = (($tmp >> 26) & 0x1f) * 60 * 60 * 1000 /* hours */ + + (($tmp >> 20) & 0x3f) * 60 * 1000 /* minutes */ + + (($tmp >> 13) & 0x3f) * 1000 /* seconds */ + + (int)(1 / $pictureRate * (($tmp >> 7) & 0x3f) * 1000); + $foundGOP = true; + } + } while (!$foundSeqHdr || !$foundGOP); + + $this->_reader->setOffset($this->_reader->getSize()); + + do { + if (($startCode = $this->prevStartCode()) == 0x100) + $pictureCount++; + } while ($startCode != 0x1b8); + + $this->_reader->skip(4); + $tmp = $this->_reader->readUInt32BE(); + $this->_length = + (((($tmp >> 26) & 0x1f) * 60 * 60 * 1000 /* hours */ + + (($tmp >> 20) & 0x3f) * 60 * 1000 /* minutes */ + + (($tmp >> 13) & 0x3f) * 1000 /* seconds */ + + (int)(1 / $pictureRate * (($tmp >> 7) & 0x3f) * 1000)) - $startTime + + (int)(1 / $pictureRate * $pictureCount * 1000)) / 1000; + } + + /** + * Returns the exact playtime in seconds. + * + * @return integer + */ + public function getLength() { return $this->_length; } + + /** + * Returns the exact playtime given in seconds as a string in the form of + * [hours]:minutes:seconds.milliseconds. + * + * @param integer $seconds The playtime in seconds. + * @return string + */ + public function getFormattedLength() + { + return $this->formatTime($this->getLength()); + } +}