diff --git a/src/ASF.php b/src/ASF.php index c8a62a0..4aec4d9 100644 --- a/src/ASF.php +++ b/src/ASF.php @@ -38,11 +38,9 @@ /**#@+ @ignore */ require_once("Reader.php"); -require_once("ASF/Object.php"); -require_once("ASF/HeaderObject.php"); -require_once("ASF/ContentDescriptionObject.php"); -require_once("ASF/ExtendedContentDescriptionObject.php"); -require_once("ASF/FilePropertiesObject.php"); +require_once("ASF/Object/Container.php"); +//require_once("ASF/Object/Header.php"); +//require_once("ASF/Object/Data.php"); /**#@-*/ /** @@ -52,9 +50,6 @@ require_once("ASF/FilePropertiesObject.php"); * types of information ranging from audio and video to script commands and * developer defined custom streams. * - * This is hardly a full implementation of a ASF reader but provides you with - * the ability to read metadata out of an ASF based file (WMA, WMV, etc). - * * The ASF file consists of code blocks that are called content objects. Each * of these objects have a format of their own. They may contain other objects * or other specific data. Each supported object has been implemented as their @@ -67,58 +62,92 @@ require_once("ASF/FilePropertiesObject.php"); * @license http://code.google.com/p/php-reader/wiki/License New BSD License * @version $Rev$ */ -class ASF +class ASF extends ASF_Object_Container { - /** @var Reader */ - private $_reader; + const HEADER = "75b22630-668e-11cf-a6d9-00aa0062ce6c"; + const DATA = "75b22636-668e-11cf-a6d9-00aa0062ce6c"; + const SIMPLE_INDEX = "33000890-e5b1-11cf-89f4-00a0c90349cb"; + const INDEX = "d6e229d3-35da-11d1-9034-00a0c90349be"; + const MEDIA_OBJECT_INDEX = "feb103f8-12ad-4c64-840f-2a1d2f7ad48c"; + const TIMECODE_INDEX = "3cb73fd0-0c4a-4803-953d-edf7b6228f0c"; + + /** @var string */ + private $_filename; /** - * Constructs the ASF class with given file. + * Constructs the ASF class with given file and options. * - * @param string $filename The path to the file. + * The following options are currently recognized: + * o encoding -- Indicates the encoding that all the texts are presented + * with. By default this is set to utf-8. See the documentation of iconv + * for accepted values. + * o readonly -- Indicates that the file is read from a temporary location + * or another source it cannot be written back to. + * + * @param string $filename The path to the file or file descriptor of an + * opened file. + * @param Array $options The options array. */ - public function __construct($filename) + public function __construct($filename, $options = array()) { - $this->_reader = new Reader($filename); + $this->_reader = new Reader($this->_filename = $filename); + $this->setOptions($options); + if ($this->getOption("encoding", false) === false) + $this->setOption("encoding", "utf-8"); + $this->setOffset(0); + $this->setSize($this->_reader->getSize()); + $this->constructObjects + (array + (self::HEADER => "Header", + self::DATA => "Data", + self::SIMPLE_INDEX => "SimpleIndex", + self::INDEX => "Index", + self::MEDIA_OBJECT_INDEX => "MediaObjectIndex", + self::TIMECODE_INDEX => "TimecodeIndex")); } /** - * Checks whether there are objects left in the stream. Returns - * true if there are objects left in the stream, false - * otherwise. + * Returns the mandatory header object contained in this file. * - * @return boolean + * @return ASF_Object_Header */ - public function hasObjects() + public function getHeader() { - return $this->_reader->available(); + $header = $this->getObjectsByIdentifier(self::HEADER); + return $header[0]; } /** - * Returns the next ASF object or false if end of stream has been - * reached. Returned objects are of the type ASF_Object or of any of its child - * types. + * Returns the mandatory data object contained in this file. * - * @todo Only the ASF_Header_Object top level object is regognized. - * @return ASF_Object + * @return ASF_Object_Data */ - public function nextObject() + public function getData() { - $object = false; - if ($this->hasObjects()) { - $guid = $this->_reader->readGUID(); - $size = $this->_reader->readInt64LE(); - $offset = $this->_reader->getOffset(); - - switch ($guid) { - case "75b22630-668e-11cf-a6d9-00aa0062ce6c": /* ASF_Header_Object */ - $object = new ASF_HeaderObject($this->_reader, $guid, $size); - break; - default: - $object = new ASF_Object($this->_reader, $guid, $size); - } - $this->_reader->setOffset($offset - 24 + $size); - } - return $object; + $data = $this->getObjectsByIdentifier(self::DATA); + return $data[0]; + } + + /** + * Returns an array of index objects contained in this file. + * + * @return Array + */ + public function getIndices() + { + return $this->getObjectsByIdentifier + (self::SIMPLE_INDEX . "|" . self::INDEX . "|" . + self::MEDIA_OBJECT_INDEX . "|" . self::TIMECODE_INDEX); + } + + /** + * Writes the changes back to the original media file. + * + * Please note: currently the method writes only Content Description and + * Extended Content Description Objects. + */ + public function write() + { + throw new ASF_Exception("Not yet supported"); } } diff --git a/src/ASF/Exception.php b/src/ASF/Exception.php new file mode 100644 index 0000000..cc29c08 --- /dev/null +++ b/src/ASF/Exception.php @@ -0,0 +1,51 @@ + + * @copyright Copyright (c) 2008 The PHP Reader Project Workgroup + * @license http://code.google.com/p/php-reader/wiki/License New BSD License + * @version $Rev$ + */ +class ASF_Exception extends Exception +{ +} diff --git a/src/ASF/FilePropertiesObject.php b/src/ASF/FilePropertiesObject.php deleted file mode 100644 index a1db3d7..0000000 --- a/src/ASF/FilePropertiesObject.php +++ /dev/null @@ -1,213 +0,0 @@ -ASF_File_Properties_Object object implementation. This object - * contains various information about the ASF file. - * - * @package php-reader - * @subpackage ASF - * @author Sven Vollbehr - * @copyright Copyright (c) 2006-2008 The PHP Reader Project Workgroup - * @license http://code.google.com/p/php-reader/wiki/License New BSD License - * @version $Rev$ - */ -final class ASF_FilePropertiesObject extends ASF_Object -{ - /** @var string */ - private $_fileId; - - /** @var string */ - private $_fileSize; - - /** @var string */ - private $_creationDate; - - /** @var string */ - private $_dataPacketsCount; - - /** @var string */ - private $_playDuration; - - /** @var string */ - private $_sendDuration; - - /** @var string */ - private $_preroll; - - /** @var string */ - private $_flags; - - /** @var string */ - private $_minimumDataPacketSize; - - /** @var string */ - private $_maximumDataPacketSize; - - /** @var string */ - private $_maximumBitrate; - - /** - * Constructs the class with given parameters and reads object related data - * from the ASF file. - * - * @param Reader $reader The reader object. - * @param string $id The object GUID identifier. - * @param integer $size The object size. - */ - public function __construct($reader, $id, $size) - { - parent::__construct($reader, $id, $size); - - $this->_fileId = $this->_reader->readGUID(); - $this->_fileSize = $this->_reader->readInt64LE(); - $this->_creationDate = $this->_reader->readInt64LE(); - $this->_dataPacketsCount = $this->_reader->readInt64LE(); - - $seconds = floor($this->_reader->readInt64LE() / 10000000); - $minutes = floor($seconds / 60); - $hours = floor($minutes / 60); - $this->_playDuration = - ($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); - - $seconds = floor($this->_reader->readInt64LE() / 10000000); - $minutes = floor($seconds / 60); - $hours = floor($minutes / 60); - $this->_sendDuration = - ($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); - - $this->_preroll = $this->_reader->readInt64LE(); - $this->_flags = $this->_reader->readUInt32LE(); - $this->_minimumDataPacketSize = $this->_reader->readUInt32LE(); - $this->_maximumDataPacketSize = $this->_reader->readUInt32LE(); - $this->_maximumBitrate = $this->_reader->readUInt32LE(); - } - - /** - * Returns the file id field. - * - * @return integer - */ - public function getFileId() { return $this->_fileId; } - - /** - * Returns the file size field. - * - * @return integer - */ - public function getFileSize() { return $this->_fileSize; } - - /** - * Returns the creation date field. - * - * @return integer - */ - public function getCreationDate() { return $this->_creationDate; } - - /** - * Returns the data packets field. - * - * @return integer - */ - public function getDataPacketsCount() { return $this->_dataPacketsCount; } - - /** - * Returns the play duration field. - * - * @return integer - */ - public function getPlayDuration() { return $this->_playDuration; } - - /** - * Returns the send duration field. - * - * @return integer - */ - public function getSendDuration() { return $this->_sendDuration; } - - /** - * Returns the preroll field. - * - * @return integer - */ - public function getPreroll() { return $this->_preroll; } - - /** - * Returns the flags field. - * - * @return integer - */ - public function getFlags() { return $this->_flags; } - - /** - * Returns the minimum data packet size field. - * - * @return integer - */ - public function getMinimumDataPacketSize() - { - return $this->_minimumDataPacketSize; - } - - /** - * Returns the maximum data packet size field. - * - * @return integer - */ - public function getMaximumDataPacketSize() - { - return $this->_maximumDataPacketSize; - } - - /** - * Returns the maximum bitrate field. - * - * @return integer - */ - public function getMaximumBitrate() { return $this->_maximumBitrate; } -} diff --git a/src/ASF/HeaderObject.php b/src/ASF/HeaderObject.php deleted file mode 100644 index 77cb75b..0000000 --- a/src/ASF/HeaderObject.php +++ /dev/null @@ -1,154 +0,0 @@ -ASF_Header_Object object implementation. This object contains - * objects that give information about the file. See corresponding object - * classes for more. - * - * @package php-reader - * @subpackage ASF - * @author Sven Vollbehr - * @copyright Copyright (c) 2006-2008 The PHP Reader Project Workgroup - * @license http://code.google.com/p/php-reader/wiki/License New BSD License - * @version $Rev$ - */ -final class ASF_HeaderObject extends ASF_Object -{ - /** @var integer */ - private $_objectCount; - - /** - * @internal Internal variable to have the start of the stream stored in. - * @var integer - */ - private $_readerSOffset; - - /** - * @internal Internal variable to have the current position of the stream - * pointer stored in. - * @var integer - */ - private $_readerCOffset; - - /** - * Constructs the class with given parameters and reads object related data - * from the ASF file. - * - * @param Reader $reader The reader object. - * @param string $id The object GUID identifier. - * @param integer $size The object size. - */ - public function __construct($reader, $id, $size) - { - parent::__construct($reader, $id, $size); - - $this->_readerSOffset = $this->_reader->getOffset(); - $this->_objectCount = $this->_reader->readUInt32LE(); - $this->_reader->skip(2); - $this->_readerCOffset = $this->_reader->getOffset(); - } - - /** - * Returns the number of standard ASF header objects this object contains. - * - * @return integer - */ - public function getObjectCount() { return $this->_objectCount; } - - /** - * Checks whether there is more to be read within the bounds of the parent - * object size. Returns true if there are child objects unread, - * false otherwise. - * - * @return boolean - */ - public function hasChildObjects() - { - return ($this->_readerSOffset + $this->_size) > $this->_readerCOffset; - } - - /** - * Returns the next ASF object or false if end of stream has been - * reached. Returned objects are of the type ASFObject or of any of - * the other object types that inherit from that base class. - * - * @todo Only limited subset of possible child objects are regognized. - * @return ASF_Object|false - */ - public function nextChildObject() - { - $object = false; - if ($this->hasChildObjects()) { - $this->_reader->setOffset($this->_readerCOffset); - $guid = $this->_reader->readGUID(); - $size = $this->_reader->readInt64LE(); - $offset = $this->_reader->getOffset(); - switch ($guid) { - /* ASF_Content_Description_Object */ - case "75b22633-668e-11cf-a6d9-00aa0062ce6c": - $object = - new ASF_ContentDescriptionObject($this->_reader, $guid, $size); - break; - /* ASF_Header_Extension_Object */ - case "5fbf03b5-a92e-11cf-8ee3-00c00c205365": - $this->_reader->skip(48); - $this->_readerCOffset = $this->_reader->getOffset(); - $object = $this->nextChildObject(); - break; - /* ASF_Extended_Content_Description_Object */ - case "d2d0a440-e307-11d2-97f0-00a0c95ea850": - $object = new ASF_ExtendedContentDescriptionObject - ($this->_reader, $guid, $size); - break; - /* ASF_File_Properties_Object */ - case "8cabdca1-a947-11cf-8ee4-00c00c205365": - $object = new ASF_FilePropertiesObject($this->_reader, $guid, $size); - break; - default: // not implemented - $object = new ASF_Object($this->_reader, $guid, $size); - } - $this->_reader->setOffset(($this->_readerCOffset = $offset - 24 + $size)); - } - return $object; - } -} diff --git a/src/ASF/Object.php b/src/ASF/Object.php index fd3cb8c..c3c0a60 100644 --- a/src/ASF/Object.php +++ b/src/ASF/Object.php @@ -36,8 +36,14 @@ * @version $Id$ */ +/**#@+ @ignore */ +require_once("ASF/Exception.php"); +/**#@-*/ + /** - * The base class for all ASF objects. + * The base unit of organization for ASF files is called the ASF object. It + * consists of a 128-bit GUID for the object, a 64-bit integer object size, and + * the variable-length object data. * * @package php-reader * @subpackage ASF @@ -55,34 +61,166 @@ class ASF_Object */ protected $_reader; + /** + * The options array. + * + * @var Array + */ + protected $_options; + + /** @var integer */ + private $_offset = -1; + /** @var string */ private $_id; - /** - * The object size in bytes. - * - * @var integer - */ - protected $_size; + /** @var integer */ + private $_size = -1; + + /** @var ASF_Object */ + private $_parent = null; /** - * Constructs the class with given parameters. + * Constructs the class with given parameters and options. * - * @param Reader $reader The reader object. - * @param string $id The object GUID identifier. - * @param integer $size The object size. + * @param Reader $reader The reader object. + * @param Array $options The options array. */ - public function __construct($reader, $id, $size) + public function __construct($reader, &$options = array()) { $this->_reader = $reader; - $this->_id = $id; - $this->_size = $size; + $this->_options = $options; + $this->_offset = $this->_reader->getOffset(); + $this->_id = $this->_reader->readGUID(); + $this->_size = $this->_reader->readInt64LE(); } + /** + * Returns the file offset to box start, or -1 if the box was created on heap. + * + * @return integer + */ + public function getOffset() { return $this->_offset; } + + /** + * Sets the file offset where the box starts. + * + * @param integer $offset The file offset to box start. + */ + public function setOffset($offset) { $this->_offset = $offset; } + /** * Returns the GUID of the ASF object. * * @return string */ public function getIdentifier() { return $this->_id; } + + /** + * Set the GUID of the ASF object. + * + * @param string $id The GUID + */ + public function setIdentifier($id) { $this->_id = $id; } + + /** + * Returns the object size in bytes, including the header. + * + * @return integer + */ + public function getSize() { return $this->_size; } + + /** + * Sets the box size. The size must include the header. + * + * @param integer $size The box size. + */ + public function setSize($size) + { + if ($this->_parent !== null) + $this->_parent->setSize + (($this->_parent->getSize() > 0 ? $this->_parent->getSize() : 0) + + $size - ($this->_size > 0 ? $this->_size : 0)); + $this->_size = $size; + } + + /** + * Returns the options array. + * + * @return Array + */ + public function getOptions() { return $this->_options; } + + /** + * Returns the given option value, or the default value if the option is not + * defined. + * + * @param string $option The name of the option. + * @param mixed $defaultValue The default value to be returned. + */ + public function getOption($option, $defaultValue = false) + { + if (isset($this->_options[$option])) + return $this->_options[$option]; + return $defaultValue; + } + + /** + * Sets the options array. See {@link ISO14496} class for available options. + * + * @param Array $options The options array. + */ + public function setOptions(&$options) { $this->_options = $options; } + + /** + * Sets the given option the given value. + * + * @param string $option The name of the option. + * @param mixed $value The value to set for the option. + */ + public function setOption($option, $value) + { + $this->_options[$option] = $value; + } + + /** + * Returns the parent object containing this box. + * + * @return ASF_Object + */ + public function getParent() { return $this->_parent; } + + /** + * Sets the parent containing object. + * + * @param ASF_Object $parent The parent object. + */ + public function setParent(&$parent) { $this->_parent = $parent; } + + /** + * Magic function so that $obj->value will work. + * + * @param string $name The field name. + * @return mixed + */ + public function __get($name) + { + if (method_exists($this, "get" . ucfirst($name))) + return call_user_func(array($this, "get" . ucfirst($name))); + throw new ASF_Exception("Unknown field: " . $name); + } + + /** + * Magic function so that assignments with $obj->value will work. + * + * @param string $name The field name. + * @param string $value The field value. + * @return mixed + */ + public function __set($name, $value) + { + if (method_exists($this, "set" . ucfirst($name))) + call_user_func(array($this, "set" . ucfirst($name)), $value); + else throw new ASF_Exception("Unknown field: " . $name); + } } diff --git a/src/ASF/Object/BitrateMutualExclusion.php b/src/ASF/Object/BitrateMutualExclusion.php new file mode 100644 index 0000000..8b4dc36 --- /dev/null +++ b/src/ASF/Object/BitrateMutualExclusion.php @@ -0,0 +1,100 @@ +Bitrate Mutual Exclusion Object identifies video streams that have + * a mutual exclusion relationship to each other (in other words, only one of + * the streams within such a relationship can be streamed at any given time and + * the rest are ignored). One instance of this object must be present for each + * set of objects that contains a mutual exclusion relationship. All video + * streams in this relationship must have the same frame size. The exclusion + * type is used so that implementations can allow user selection of common + * choices, such as bit rate. + * + * @package php-reader + * @subpackage ASF + * @author Sven Vollbehr + * @copyright Copyright (c) 2008 The PHP Reader Project Workgroup + * @license http://code.google.com/p/php-reader/wiki/License New BSD License + * @version $Rev$ + */ +final class ASF_Object_BitrateMutualExclusion extends ASF_Object +{ + const MUTEX_LANGUAGE = "d6e22a00-35da-11d1-9034-00a0c90349be"; + const MUTEX_BITRATE = "d6e22a01-35da-11d1-9034-00a0c90349be"; + const MUTEX_UNKNOWN = "d6e22a02-35da-11d1-9034-00a0c90349be"; + + /** @var string */ + private $_exclusionType; + + /** @var Array */ + private $_streamNumbers = array(); + + /** + * Constructs the class with given parameters and reads object related data + * from the ASF file. + * + * @param Reader $reader The reader object. + * @param Array $options The options array. + */ + public function __construct($reader, &$options = array()) + { + parent::__construct($reader, $options); + $this->_exclusionType = $this->_reader->readGUID(); + $streamNumbersCount = $this->_reader->readUInt16LE(); + for ($i = 0; $i < $streamNumbersCount; $i++) + $this->_streamNumbers[] = $this->_reader->readUInt16LE(); + } + + /** + * Returns the nature of the mutual exclusion relationship. + * + * @return string + */ + public function getExclusionType() { return $this->_exclusionType; } + + /** + * Returns an array of stream numbers. + * + * @return Array + */ + public function getStreamNumbers() { return $this->_streamNumbers; } +} diff --git a/src/ASF/Object/CodecList.php b/src/ASF/Object/CodecList.php new file mode 100644 index 0000000..5227474 --- /dev/null +++ b/src/ASF/Object/CodecList.php @@ -0,0 +1,98 @@ +Codec List Object provides user-friendly information about the + * codecs and formats used to encode the content found in the ASF file. + * + * @package php-reader + * @subpackage ASF + * @author Sven Vollbehr + * @copyright Copyright (c) 2008 The PHP Reader Project Workgroup + * @license http://code.google.com/p/php-reader/wiki/License New BSD License + * @version $Rev$ + */ +final class ASF_Object_CodecList extends ASF_Object +{ + const VIDEO_CODEC = 0x1; + const AUDIO_CODEC = 0x2; + const UNKNOWN_CODEC = 0xffff; + + /** @var Array */ + private $_entries = array(); + + /** + * Constructs the class with given parameters and reads object related data + * from the ASF file. + * + * @param Reader $reader The reader object. + * @param Array $options The options array. + */ + public function __construct($reader, &$options = array()) + { + parent::__construct($reader, $options); + + $this->_reader->skip(16); + $codecEntriesCount = $this->_reader->readUInt32LE(); + for ($i = 0; $i < $codecEntriesCount; $i++) { + $entry = array("type" => $this->_reader->readUInt16LE()); + $codecNameLength = $this->_reader->readUInt16LE() * 2; + $entry["codecName"] = iconv + ("utf-16le", $this->getOption("encoding"), + $this->_reader->readString16LE($codecNameLength)); + $codecDescriptionLength = $this->_reader->readUInt16LE() * 2; + $entry["codecDescription"] = iconv + ("utf-16le", $this->getOption("encoding"), + $this->_reader->readString16LE($codecDescriptionLength)); + $codecInformationLength = $this->_reader->readUInt16LE(); + $entry["codecInformation"] = + $this->_reader->read($codecInformationLength); + $this->_entries[] = $entry; + } + } + + /** + * Returns the array of codec entries. + * + * @return Array + */ + public function getEntries() { return $this->_entries; } +} diff --git a/src/ASF/Object/Container.php b/src/ASF/Object/Container.php new file mode 100644 index 0000000..e9059c7 --- /dev/null +++ b/src/ASF/Object/Container.php @@ -0,0 +1,177 @@ + + * @copyright Copyright (c) 2008 The PHP Reader Project Workgroup + * @license http://code.google.com/p/php-reader/wiki/License New BSD License + * @version $Rev$ + */ +abstract class ASF_Object_Container extends ASF_Object +{ + /** @var Array */ + private $_objects = array(); + + /** + * Reads and constructs the objects found within this object. + */ + protected function constructObjects($defaultclassnames = array()) + { + while (true) { + $offset = $this->_reader->getOffset(); + if ($offset >= $this->getOffset() + $this->getSize()) + break; + $guid = $this->_reader->readGUID(); + $size = $this->_reader->readInt64LE(); + + $this->_reader->setOffset($offset); + if (isset($defaultclassnames[$guid])) { + if (@fopen($filename = "ASF/Object/" . $defaultclassnames[$guid] . + ".php", "r", true) !== false) + require_once($filename); + if (class_exists + ($classname = "ASF_Object_" . $defaultclassnames[$guid])) + $object = new $classname($this->_reader, $this->_options); + else + $object = new ASF_Object($this->_reader, $this->_options); + } else + $object = new ASF_Object($this->_reader, $this->_options); + $object->setParent($this); + if (!$this->hasObject($object->getIdentifier())) + $this->_objects[$object->getIdentifier()] = array(); + $this->_objects[$object->getIdentifier()][] = $object; + $this->_reader->setOffset($offset + $size); + } + } + + /** + * Checks whether the object with given GUID is present in the file. Returns + * true if one or more objects are present, false + * otherwise. + * + * @return boolean + */ + public function hasObject($identifier) + { + return isset($this->_objects[$identifier]); + } + + /** + * Returns all the objects the file contains as an associate array. The object + * identifiers work as keys having an array of ASF objects as associated + * value. + * + * @return Array + */ + public function getObjects() + { + return $this->_objects; + } + + /** + * Returns an array of objects matching the given object GUID or an empty + * array if no object matched the identifier. + * + * The identifier may contain wildcard characters "*" and "?". The asterisk + * matches against zero or more characters, and the question mark matches any + * single character. + * + * Please note that one may also use the shorthand $obj->identifier to access + * the first box with the identifier given. Wildcards cannot be used with + * the shorthand and they will not work with user defined uuid types. + * + * @return Array + */ + public function getObjectsByIdentifier($identifier) + { + $matches = array(); + $searchPattern = "/^" . + str_replace(array("*", "?"), array(".*", "."), $identifier) . "$/i"; + foreach ($this->_objects as $identifier => $objects) + if (preg_match($searchPattern, $identifier)) + foreach ($objects as $object) + $matches[] = $object; + return $matches; + } + + /** + * Adds a new object into the current object and returns it. + * + * @param ASF_Object The object to add + * @return ASF_Object + */ + public function addObject($object) + { + $object->setParent($this); + $object->setOptions($this->_options); + if (!$this->hasObject($object->getIdentifier())) + $this->_objects[$object->getIdentifier()] = array(); + return $this->_objects[$object->getIdentifier()][] = $object; + } + + /** + * Override magic function so that $obj->value will work as expected. + * + * The method first attempts to call the appropriate getter method. If no + * field with given name is found, the method attempts to return the right + * object instead. In other words, calling $obj->value will attempt to return + * the first object returned by $this->getObjectsByIdentifier(self::value). + * + * @param string $name The field or object name. + * @return mixed + */ + public function __get($name) + { + if (method_exists($this, "get" . ucfirst($name))) + return call_user_func(array($this, "get" . ucfirst($name))); + if (defined($constname = get_class($this) . "::" . strtoupper + (preg_replace("/[A-Z]/", "_$0", $name)))) { + $objects = $this->getObjectsByIdentifier(constant($constname)); + if (isset($objects[0])) + return $objects[0]; + } + throw new ASF_Exception("Unknown field/object: " . $name); + } +} diff --git a/src/ASF/Object/ContentBranding.php b/src/ASF/Object/ContentBranding.php new file mode 100644 index 0000000..09fe26f --- /dev/null +++ b/src/ASF/Object/ContentBranding.php @@ -0,0 +1,135 @@ +Content Branding Object stores branding data for an ASF file, + * including information about a banner image and copyright associated with the + * file. + * + * @package php-reader + * @subpackage ASF + * @author Sven Vollbehr + * @copyright Copyright (c) 2008 The PHP Reader Project Workgroup + * @license http://code.google.com/p/php-reader/wiki/License New BSD License + * @version $Rev$ + */ +final class ASF_Object_ContentBranding extends ASF_Object +{ + /** Indicates that there is no banner */ + const TYPE_NONE = 0; + + /** Indicates that the data represents a bitmap */ + const TYPE_BMP = 1; + + /** Indicates that the data represents a JPEG */ + const TYPE_JPEG = 2; + + /** Indicates that the data represents a GIF */ + const TYPE_GIF = 3; + + + /** @var integer */ + private $_bannerImageType; + + /** @var string */ + private $_bannerImageData; + + /** @var string */ + private $_bannerImageUrl; + + /** @var string */ + private $_copyrightUrl; + + /** + * Constructs the class with given parameters and reads object related data + * from the ASF file. + * + * @param Reader $reader The reader object. + * @param Array $options The options array. + */ + public function __construct($reader, &$options = array()) + { + parent::__construct($reader, $options); + + $this->_bannerImageType = $this->_reader->readUInt32LE(); + $bannerImageDataSize = $this->_reader->readUInt32LE(); + $this->_bannerImageData = $this->_reader->read($bannerImageDataSize); + $bannerImageUrlLength = $this->_reader->readUInt32LE(); + $this->_bannerImageUrl = $this->_reader->read($bannerImageUrlLength); + $copyrightUrlLength = $this->_reader->readUInt32LE(); + $this->_copyrightUrl = $this->_reader->read($copyrightUrlLength); + } + + /** + * Returns the type of data contained in the Banner Image Data. Valid + * values are 0 to indicate that there is no banner image data; 1 to indicate + * that the data represent a bitmap; 2 to indicate that the data represents a + * JPEG; and 3 to indicate that the data represents a GIF. If this value is + * set to 0, then the Banner Image Data Size field is set to 0, and the + * Banner Image Data field is empty. + * + * @return integer + */ + public function getBannerImageType() { return $this->_bannerImageType; } + + /** + * Returns the entire banner image, including the header for the appropriate + * image format. + * + * @return string + */ + public function getBannerImageData() { return $this->_bannerImageData; } + + /** + * Returns, if present, a link to more information about the banner image. + * + * @return string + */ + public function getBannerImageUrl() { return $this->_bannerImageUrl; } + + /** + * Returns, if present, a link to more information about the copyright for the + * content. + * + * @return string + */ + public function getCopyrightUrl() { return $this->_copyrightUrl; } +} diff --git a/src/ASF/ContentDescriptionObject.php b/src/ASF/Object/ContentDescription.php similarity index 68% rename from src/ASF/ContentDescriptionObject.php rename to src/ASF/Object/ContentDescription.php index ed0fd2c..cfa4a3e 100644 --- a/src/ASF/ContentDescriptionObject.php +++ b/src/ASF/Object/ContentDescription.php @@ -37,13 +37,14 @@ */ /**#@+ @ignore */ -require_once("Object.php"); +require_once("ASF/Object.php"); /**#@-*/ /** - * The ASF_Content_Description_Object object implementation. This object - * contains five core attribute fields giving more information about the file: - * title, author, description, copyright and rating. + * The Content Description Object lets authors record well-known data + * describing the file and its contents. This object is used to store standard + * bibliographic information such as title, author, copyright, description, and + * rating information. This information is pertinent to the entire file. * * @package php-reader * @subpackage ASF @@ -52,7 +53,7 @@ require_once("Object.php"); * @license http://code.google.com/p/php-reader/wiki/License New BSD License * @version $Rev$ */ -final class ASF_ContentDescriptionObject extends ASF_Object +final class ASF_Object_ContentDescription extends ASF_Object { /** @var string */ private $_title; @@ -73,13 +74,12 @@ final class ASF_ContentDescriptionObject extends ASF_Object * Constructs the class with given parameters and reads object related data * from the ASF file. * - * @param Reader $reader The reader object. - * @param string $id The object GUID identifier. - * @param integer $size The object size. + * @param Reader $reader The reader object. + * @param Array $options The options array. */ - public function __construct($reader, $id, $size) + public function __construct($reader, &$options = array()) { - parent::__construct($reader, $id, $size); + parent::__construct($reader, $options); $titleLen = $this->_reader->readUInt16LE(); $authorLen = $this->_reader->readUInt16LE(); @@ -87,41 +87,53 @@ final class ASF_ContentDescriptionObject extends ASF_Object $descriptionLen = $this->_reader->readUInt16LE(); $ratingLen = $this->_reader->readUInt16LE(); - $this->_title = $this->_reader->readString16LE($titleLen); - $this->_author = $this->_reader->readString16LE($authorLen); - $this->_copyright = $this->_reader->readString16LE($copyrightLen); - $this->_description = $this->_reader->readString16LE($descriptionLen); - $this->_rating = $this->_reader->readString16LE($ratingLen); + $this->_title = iconv + ("utf-16le", $this->getOption("encoding"), + $this->_reader->readString16LE($titleLen)); + $this->_author = iconv + ("utf-16le", $this->getOption("encoding"), + $this->_reader->readString16LE($authorLen)); + $this->_copyright = iconv + ("utf-16le", $this->getOption("encoding"), + $this->_reader->readString16LE($copyrightLen)); + $this->_description = iconv + ("utf-16le", $this->getOption("encoding"), + $this->_reader->readString16LE($descriptionLen)); + $this->_rating = iconv + ("utf-16le", $this->getOption("encoding"), + $this->_reader->readString16LE($ratingLen)); } /** - * Returns the title field. + * Returns the title information. + * + * @return string */ public function getTitle() { return $this->_title; } /** - * Returns the author field. + * Returns the author information. * * @return string */ public function getAuthor() { return $this->_author; } /** - * Returns the copyright field. + * Returns the copyright information. * * @return string */ public function getCopyright() { return $this->_copyright; } /** - * Returns the description field. + * Returns the description information. * * @return string */ public function getDescription() { return $this->_description; } /** - * Returns the rating field. + * Returns the rating information. * * @return string */ diff --git a/src/ASF/Object/ContentEncryption.php b/src/ASF/Object/ContentEncryption.php new file mode 100644 index 0000000..c44d524 --- /dev/null +++ b/src/ASF/Object/ContentEncryption.php @@ -0,0 +1,117 @@ +Content Encryption Object lets authors protect content by using + * Microsoft® Digital Rights Manager version 1. + * + * @package php-reader + * @subpackage ASF + * @author Sven Vollbehr + * @copyright Copyright (c) 2008 The PHP Reader Project Workgroup + * @license http://code.google.com/p/php-reader/wiki/License New BSD License + * @version $Rev$ + */ +final class ASF_Object_ContentEncryption extends ASF_Object +{ + /** @var string */ + private $_secretData; + + /** @var string */ + private $_protectionType; + + /** @var string */ + private $_keyId; + + /** @var string */ + private $_licenseUrl; + + /** + * Constructs the class with given parameters and reads object related data + * from the ASF file. + * + * @param Reader $reader The reader object. + * @param Array $options The options array. + */ + public function __construct($reader, &$options = array()) + { + parent::__construct($reader, $options); + + $secretDataLength = $this->_reader->readUInt32LE(); + $this->_secretData = $this->_reader->read($secretDataLength); + $protectionTypeLength = $this->_reader->readUInt32LE(); + $this->_protectionType = $this->_reader->readString8($protectionTypeLength); + $keyIdLength = $this->_reader->readUInt32LE(); + $this->_keyId = $this->_reader->readString8($keyIdLength); + $licenseUrlLength = $this->_reader->readUInt32LE(); + $this->_licenseUrl = $this->_reader->readString8($licenseUrlLength); + } + + /** + * Returns the secret data. + * + * @return string + */ + public function getSecretData() { return $this->_secretData; } + + /** + * Returns the type of protection mechanism used. The value of this field + * is set to "DRM". + * + * @return string + */ + public function getProtectionType() { return $this->_protectionType; } + + /** + * Returns the key ID used. + * + * @return string + */ + public function getKeyId() { return $this->_keyId; } + + /** + * Returns the URL from which a license to manipulate the content can be + * acquired. + * + * @return string + */ + public function getLicenseUrl() { return $this->_licenseUrl; } +} diff --git a/src/ASF/Object/DigitalSignature.php b/src/ASF/Object/DigitalSignature.php new file mode 100644 index 0000000..f988043 --- /dev/null +++ b/src/ASF/Object/DigitalSignature.php @@ -0,0 +1,91 @@ +Digital Signature Object lets authors sign the portion of their + * header that lies between the end of the File Properties Object and the + * beginning of the Digital Signature Object. + * + * @package php-reader + * @subpackage ASF + * @author Sven Vollbehr + * @copyright Copyright (c) 2008 The PHP Reader Project Workgroup + * @license http://code.google.com/p/php-reader/wiki/License New BSD License + * @version $Rev$ + */ +final class ASF_Object_DigitalSignature extends ASF_Object +{ + /** @var integer */ + private $_signatureType; + + /** @var string */ + private $_signatureData; + + /** + * Constructs the class with given parameters and reads object related data + * from the ASF file. + * + * @param Reader $reader The reader object. + * @param Array $options The options array. + */ + public function __construct($reader, &$options = array()) + { + parent::__construct($reader, $options); + + $this->_signatureType = $this->_reader->readUInt32LE(); + $signatureDataLength = $this->_reader->readUInt32LE(); + $this->_signatureData = $this->_reader->read($signatureDataLength); + } + + /** + * Returns the type of digital signature used. This field is set to 2. + * + * @return integer + */ + public function getSignatureType() { return $this->_signatureType; } + + /** + * Returns the digital signature data. + * + * @return string + */ + public function getSignatureData() { return $this->_signatureData; } +} diff --git a/src/ASF/Object/ErrorCorrection.php b/src/ASF/Object/ErrorCorrection.php new file mode 100644 index 0000000..a54e182 --- /dev/null +++ b/src/ASF/Object/ErrorCorrection.php @@ -0,0 +1,100 @@ +Error Correction Object defines the error correction method. This + * enables different error correction schemes to be used during content + * creation. The Error Correction Object contains provisions for opaque + * information needed by the error correction engine for recovery. For example, + * if the error correction scheme were a simple N+1 parity scheme, then the + * value of N would have to be available in this object. + * + * Note that this does not refer to the same thing as the Error Correction + * Type field in the {@link ASF_Object_StreamProperties Stream Properties + * Object}. + * + * @package php-reader + * @subpackage ASF + * @author Sven Vollbehr + * @copyright Copyright (c) 2008 The PHP Reader Project Workgroup + * @license http://code.google.com/p/php-reader/wiki/License New BSD License + * @version $Rev$ + */ +final class ASF_Object_ErrorCorrection extends ASF_Object +{ + /** @var string */ + private $_type; + + /** @var string */ + private $_data; + + /** + * Constructs the class with given parameters and reads object related data + * from the ASF file. + * + * @param Reader $reader The reader object. + * @param Array $options The options array. + */ + public function __construct($reader, &$options = array()) + { + parent::__construct($reader, $options); + + $this->_type = $this->_reader->readGUID(); + $dataLength = $this->_reader->readUInt32LE(); + $this->_data = $this->_reader->read($dataLength); + } + + /** + * Returns the type of error correction. + * + * @return string + */ + public function getType() { return $this->_type; } + + /** + * Returns the data specific to the error correction scheme. The structure for + * the Error Correction Data field is determined by the value stored in + * the Error Correction Type field. + * + * @return Array + */ + public function getData() { return $this->_data; } +} diff --git a/src/ASF/ExtendedContentDescriptionObject.php b/src/ASF/Object/ExtendedContentDescription.php similarity index 87% rename from src/ASF/ExtendedContentDescriptionObject.php rename to src/ASF/Object/ExtendedContentDescription.php index d59bbce..3a067f5 100644 --- a/src/ASF/ExtendedContentDescriptionObject.php +++ b/src/ASF/Object/ExtendedContentDescription.php @@ -37,7 +37,7 @@ */ /**#@+ @ignore */ -require_once("Object.php"); +require_once("ASF/Object.php"); /**#@-*/ /** @@ -52,7 +52,7 @@ require_once("Object.php"); * @license http://code.google.com/p/php-reader/wiki/License New BSD License * @version $Rev$ */ -final class ASF_ExtendedContentDescriptionObject extends ASF_Object +final class ASF_Object_ExtendedContentDescription extends ASF_Object { /** @var Array */ private $_contentDescriptors = array(); @@ -62,24 +62,26 @@ final class ASF_ExtendedContentDescriptionObject extends ASF_Object * from the ASF file. * * @param Reader $reader The reader object. - * @param string $id The object GUID identifier. - * @param integer $size The object size. + * @param Array $options The options array. */ - public function __construct($reader, $id, $size) + public function __construct($reader, &$options = array()) { - parent::__construct($reader, $id, $size); + parent::__construct($reader, $options); $contentDescriptorsCount = $this->_reader->readUInt16LE(); for ($i = 0; $i < $contentDescriptorsCount; $i++) { $nameLen = $this->_reader->readUInt16LE(); - $name = $this->_reader->readString16LE($nameLen); + $name = iconv + ("utf-16le", $this->getOption("encoding"), + $this->_reader->readString16LE($nameLen)); $valueDataType = $this->_reader->readUInt16LE(); $valueLen = $this->_reader->readUInt16LE(); switch ($valueDataType) { case 0: case 1: // string - $this->_contentDescriptors[$name] = - $this->_reader->readString16LE($valueLen); + $this->_contentDescriptors[$name] = iconv + ("utf-16le", $this->getOption("encoding"), + $this->_reader->readString16LE($valueLen)); break; case 2: // bool case 3: // 32-bit integer diff --git a/src/ASF/Object/ExtendedContentEncryption.php b/src/ASF/Object/ExtendedContentEncryption.php new file mode 100644 index 0000000..11eeba0 --- /dev/null +++ b/src/ASF/Object/ExtendedContentEncryption.php @@ -0,0 +1,80 @@ +Extended Content Encryption Object lets authors protect content by + * using the Windows Media Rights Manager 7 Software Development Kit (SDK). + * + * @package php-reader + * @subpackage ASF + * @author Sven Vollbehr + * @copyright Copyright (c) 2008 The PHP Reader Project Workgroup + * @license http://code.google.com/p/php-reader/wiki/License New BSD License + * @version $Rev$ + */ +final class ASF_Object_ExtendedContentEncryption extends ASF_Object +{ + /** @var string */ + private $_data; + + /** + * Constructs the class with given parameters and reads object related data + * from the ASF file. + * + * @param Reader $reader The reader object. + * @param Array $options The options array. + */ + public function __construct($reader, &$options = array()) + { + parent::__construct($reader, $options); + + $dataSize = $this->_reader->readUInt32LE(); + $this->_data = $this->_reader->read($dataSize); + } + + /** + * Returns the array of bytes required by the DRM client to manipulate the + * protected content. + * + * @return string + */ + public function getData() { return $this->_data; } +} diff --git a/src/ASF/Object/ExtendedStreamProperties.php b/src/ASF/Object/ExtendedStreamProperties.php new file mode 100644 index 0000000..2bcbce2 --- /dev/null +++ b/src/ASF/Object/ExtendedStreamProperties.php @@ -0,0 +1,405 @@ +Extended Stream Properties Object defines additional optional + * properties and characteristics of a digital media stream that are not + * described in the Stream Properties Object. + * + * Typically, the basic Stream Properties Object is present in the + * Header Object, and the Extended Stream Properties Object is + * present in the Header Extension Object. Sometimes, however, the + * Stream Properties Object for a stream may be embedded inside the + * Extended Stream Properties Object for that stream. This approach + * facilitates the creation of backward-compatible content. + * + * This object has an optional provision to include application-specific or + * implementation-specific data attached to the payloads of each digital media + * sample stored within a Data Packet. This data can be looked at as + * digital media sample properties and is stored in the Replicated Data + * field of a payload header. The Payload Extension Systems fields of the + * Extended Stream Properties Object describes what this data is and is + * necessary for that data to be parsed, if present. + * + * @package php-reader + * @subpackage ASF + * @author Sven Vollbehr + * @copyright Copyright (c) 2008 The PHP Reader Project Workgroup + * @license http://code.google.com/p/php-reader/wiki/License New BSD License + * @version $Rev$ + */ +final class ASF_Object_ExtendedStreamProperties extends ASF_Object +{ + /** + * Indicates, if set, that this digital media stream, if sent over a network, + * must be carried over a reliable data communications transport mechanism. + * This should be set for streams that cannot recover after a lost media + * object. + */ + const RELIABLE = 1; + + /** + * This flag should be set only if the stream is seekable, either by using an + * index object or by estimating according to bit rate (as can sometimes be + * done with audio). This flag pertains to this stream only rather than to the + * entire file. + */ + const SEEKABLE = 2; + + /** + * Indicates, if set, that the stream does not contain any cleanpoints. A + * cleanpoint is any point at which playback could begin without having seen + * the previous media objects. For streams that use key frames, the key frames + * would be the cleanpoints. + */ + const NO_CLEANPOINT = 4; + + /** + * Specifies, if set, that when a stream is joined in mid-transmission, all + * information from the most recent cleanpoint up to the current time should + * be sent before normal streaming begins at the current time. The default + * behavior (when this flag is not set) is to send only the data starting at + * the current time. This flag should only be set for streams that are coming + * from a live source. + */ + const RESEND_LIVE_CLEANPOINTS = 8; + + const AUDIO_MEDIA = "f8699e40-5b4d-11cf-a8fd-00805f5c442b"; + const VIDEO_MEDIA = "bc19efc0-5b4d-11cf-a8fd-00805f5c442b"; + const COMMAND_MEDIA = "59dacfc0-59e6-11d0-a3ac-00a0c90348f6"; + const JFIF_MEDIA = "b61be100-5b4e-11cf-a8fD-00805f5c442b"; + const DEGRADABLE_JPEG_MEDIA = "35907dE0-e415-11cf-a917-00805f5c442b"; + const FILE_TRANSFER_MEDIA = "91bd222c-f21c-497a-8b6d-5aa86bfc0185"; + const BINARY_MEDIA = "3afb65e2-47ef-40f2-ac2c-70a90d71d343"; + + const NO_ERROR_CORRECTION = "20fb5700-5b55-11cf-a8fd-00805f5c442b"; + const AUDIO_SPREAD = "bfc3cd50-618f-11cf-8bb2-00aa00b4e220"; + + /** @var integer */ + private $_startTime; + + /** @var integer */ + private $_endTime; + + /** @var integer */ + private $_dataBitrate; + + /** @var integer */ + private $_bufferSize; + + /** @var integer */ + private $_initialBufferFullness; + + /** @var integer */ + private $_alternateDataBitrate; + + /** @var integer */ + private $_alternateBufferSize; + + /** @var integer */ + private $_alternateInitialBufferFullness; + + /** @var integer */ + private $_maximumObjectSize; + + /** @var integer */ + private $_flags; + + /** @var integer */ + private $_streamNumber; + + /** @var integer */ + private $_streamLanguageIndex; + + /** @var integer */ + private $_averageTimePerFrame; + + /** @var Array */ + private $_streamNames = array(); + + /** @var Array */ + private $_payloadExtensionSystems = array(); + + /** + * Constructs the class with given parameters and reads object related data + * from the ASF file. + * + * @param Reader $reader The reader object. + * @param Array $options The options array. + */ + public function __construct($reader, &$options = array()) + { + parent::__construct($reader, $options); + + $this->_startTime = $this->_reader->readInt64LE(); + $this->_endTime = $this->_reader->readInt64LE(); + $this->_dataBitrate = $this->_reader->readUInt32LE(); + $this->_bufferSize = $this->_reader->readUInt32LE(); + $this->_initialBufferFullness = $this->_reader->readUInt32LE(); + $this->_alternateDataBitrate = $this->_reader->readUInt32LE(); + $this->_alternateBufferSize = $this->_reader->readUInt32LE(); + $this->_alternateInitialBufferFullness = $this->_reader->readUInt32LE(); + $this->_maximumObjectSize = $this->_reader->readUInt32LE(); + $this->_flags = $this->_reader->readUInt32LE(); + $this->_streamNumber = $this->_reader->readUInt16LE(); + $this->_streamLanguageIndex = $this->_reader->readUInt16LE(); + $this->_averageTimePerFrame = $this->_reader->readInt64LE(); + $streamNameCount = $this->_reader->readUInt16LE(); + $payloadExtensionSystemCount = $this->_reader->readUInt16LE(); + for ($i = 0; $i < $streamNameCount; $i++) { + $streamName = array("languageIndex" => $this->_reader->readUInt16LE()); + $streamNameLength = $this->_reader->readUInt16LE(); + $streamName["streamName"] = iconv + ("utf-16le", $this->getOption("encoding"), + $this->_reader->readString16LE($streamNameLength)); + $this->_streamNames[] = $streamName; + } + for ($i = 0; $i < $payloadExtensionSystemCount; $i++) { + $payloadExtensionSystem = array + ("extensionSystemId" => $this->_reader->readGUID(), + "extensionDataSize" => $this->_reader->readUInt16LE()); + $extensionSystemInfoLength = $this->_reader->readUInt32LE(); + $payloadExtensionSystem["extensionSystemInfo"] = iconv + ("utf-16le", $this->getOption("encoding"), + $this->_reader->readString16LE($extensionSystemInfoLength)); + $this->_payloadExtensionSystems[] = $payloadExtensionSystem; + } + } + + /** + * Returns the presentation time of the first object, indicating where this + * digital media stream starts within the context of the timeline of the ASF + * file as a whole. This time value corresponds to presentation times as they + * appear in the data packets (adjusted by the preroll). This field is given + * in units of milliseconds and can optionally be set to 0, in which case it + * will be ignored. + * + * @return integer + */ + public function getStartTime() { return $this->_startTime; } + + /** + * Returns the presentation time of the last object plus the duration of play, + * indicating where this digital media stream ends within the context of the + * timeline of the ASF file as a whole. This time value corresponds to + * presentation times as they appear in the data packets (adjusted by the + * preroll). This field is given in units of milliseconds and can optionally + * be set to 0, in which case it will be ignored. + * + * @return integer + */ + public function getEndTime() { return $this->_endTime; } + + /** + * Returns the leak rate R, in bits per second, of a leaky bucket that + * contains the data portion of the stream without overflowing, excluding all + * ASF Data Packet overhead. The size of the leaky bucket is specified by the + * value of the Buffer Size field. This field has a non-zero value. + * + * @return integer + */ + public function getDataBitrate() { return $this->_dataBitrate; } + + /** + * Returns the size B, in milliseconds, of the leaky bucket used in the + * Data Bitrate definition. + * + * @return integer + */ + public function getBufferSize() { return $this->_bufferSize; } + + /** + * Returns the initial fullness, in milliseconds, of the leaky bucket used in + * the Data Bitrate definition. This is the fullness of the buffer at + * the instant before the first bit in the stream is dumped into the bucket. + * Typically, this value is set to 0. This value shall not exceed the value in + * the Buffer Size field. + * + * @return integer + */ + public function getInitialBufferFullness() + { + return $this->_initialBufferFullness; + } + + /** + * Returns the leak rate RAlt, in bits per second, of a leaky bucket that + * contains the data portion of the stream without overflowing, excluding all + * ASF Data Packet overhead. The size of the leaky bucket is specified + * by the value of the Alternate Buffer Size field. This value is + * relevant in most scenarios where the bit rate is not exactly constant, but + * it is especially useful for streams that have highly variable bit rates. + * This field can optionally be set to the same value as the Data + * Bitrate field. + * + * @return integer + */ + public function getAlternateDataBitrate() + { + return $this->_alternateDataBitrate; + } + + /** + * Returns the size BAlt, in milliseconds, of the leaky bucket used in the + * Alternate Data Bitrate definition. This value is relevant in most + * scenarios where the bit rate is not exactly constant, but it is especially + * useful for streams that have highly variable bit rates. This field can + * optionally be set to the same value as the Buffer Size field. + * + * @return integer + */ + public function getAlternateBufferSize() + { + return $this->_alternateBufferSize; + } + + /** + * Returns the initial fullness, in milliseconds, of the leaky bucket used in + * the Alternate Data Bitrate definition. This is the fullness of the + * buffer at the instant before the first bit in the stream is dumped into the + * bucket. Typically, this value is set to 0. This value does not exceed the + * value of the Alternate Buffer Size field. + * + * @return integer + */ + public function getAlternateInitialBufferFullness() + { + return $this->_alternateInitialBufferFullness; + } + + /** + * Returns the maximum size of the largest sample stored in the data packets + * for a stream. A value of 0 means unknown. + * + * @return integer + */ + public function getMaximumObjectSize() + { + return $this->_maximumObjectSize; + } + + /** + * Returns the average time duration, measured in 100-nanosecond units, of + * each frame. This number should be rounded to the nearest integer. This + * field can optionally be set to 0 if the average time per frame is unknown + * or unimportant. It is recommended that this field be set for video. + * + * @return integer + */ + public function getAverageTimePerFrame() + { + return $this->_averageTimePerFrame; + } + + /** + * Returns the number of this stream. 0 is an invalid stream number (that is, + * other Header Objects use stream number 0 to refer to the entire file + * as a whole rather than to a specific media stream within the file). Valid + * values are between 1 and 127. + * + * @return integer + */ + public function getStreamNumber() + { + return $this->_streamNumber; + } + + /** + * Returns the language, if any, which the content of the stream uses or + * assumes. Refer to the {@link LanguageList Language List Object} description + * for the details concerning how the Stream Language Index and + * Language Index fields should be used. Note that this is an index + * into the languages listed in the Language List Object rather than a + * language identifier. + * + * @return integer + */ + public function getStreamLanguageIndex() + { + return $this->_streamLanguageIndex; + } + + /** + * Returns an array of Stream Names. Each stream name instance is potentially + * localized into a specific language. The Language Index field + * indicates the language in which the Stream Name has been written. + * + * The array contains the following keys: + * o languageIndex -- The language index + * o streamName -- The localized stream name + * + * @return Array + */ + public function getStreamNames() + { + return $this->_streamNames; + } + + /** + * Returns an array of payload extension systems. Payload extensions provide a + * way for content creators to specify kinds of data that will appear in the + * payload header for every payload from this stream. This system is used when + * stream properties must be conveyed at the media object level. The + * Replicated Data bytes in the payload header will contain these + * properties in the order in which the Payload Extension Systems + * appear in this object. A Payload Extension System must appear in the + * Extended Stream Properties Object for each type of per-media-object + * properties that will appear with the payloads for this stream. + * + * The array contains the following keys: + * o extensionSystemId -- Specifies a unique identifier for the extension + * system. + * o extensionDataSize -- Specifies the fixed size of the extension data for + * this system that will appear in the replicated data alongside every + * payload for this stream. If this extension system uses variable-size + * data, then this should be set to 0xffff. Note, however, that replicated + * data length is limited to 255 bytes, which limits the total size of all + * extension systems for a particular stream. + * o extensionSystemInfo -- Specifies additional information to describe + * this extension system (optional). + * + * @return Array + */ + public function getPayloadExtensionSystems() + { + return $this->_payloadExtensionSystems; + } +} diff --git a/src/ASF/Object/FileProperties.php b/src/ASF/Object/FileProperties.php new file mode 100644 index 0000000..5479312 --- /dev/null +++ b/src/ASF/Object/FileProperties.php @@ -0,0 +1,255 @@ +File Properties Object defines the global characteristics of the + * combined digital media streams found within the Data Object. + * + * @package php-reader + * @subpackage ASF + * @author Sven Vollbehr + * @copyright Copyright (c) 2006-2008 The PHP Reader Project Workgroup + * @license http://code.google.com/p/php-reader/wiki/License New BSD License + * @version $Rev$ + */ +final class ASF_Object_FileProperties extends ASF_Object +{ + /** + * Indicates, if set, that a file is in the process of being created (for + * example, for recording applications), and thus that various values stored + * in the header objects are invalid. It is highly recommended that + * post-processing be performed to remove this condition at the earliest + * opportunity. + */ + const BROADCAST = 1; + + /** + * Indicates, if set, that a file is seekable. Note that for files containing + * a single audio stream and a Minimum Data Packet Size field equal to + * the Maximum Data Packet Size field, this flag shall always be set to + * 1. For files containing a single audio stream and a video stream or + * mutually exclusive video streams, this flag is only set to 1 if the file + * contains a matching Simple Index Object for each regular video + * stream. + */ + const SEEKABLE = 2; + + /** @var string */ + private $_fileId; + + /** @var integer */ + private $_fileSize; + + /** @var integer */ + private $_creationDate; + + /** @var integer */ + private $_dataPacketsCount; + + /** @var integer */ + private $_playDuration; + + /** @var integer */ + private $_sendDuration; + + /** @var integer */ + private $_preroll; + + /** @var integer */ + private $_flags; + + /** @var integer */ + private $_minimumDataPacketSize; + + /** @var integer */ + private $_maximumDataPacketSize; + + /** @var integer */ + private $_maximumBitrate; + + /** + * Constructs the class with given parameters and reads object related data + * from the ASF file. + * + * @param Reader $reader The reader object. + * @param Array $options The options array. + */ + public function __construct($reader, &$options = array()) + { + parent::__construct($reader, $options); + + $this->_fileId = $this->_reader->readGUID(); + $this->_fileSize = $this->_reader->readInt64LE(); + $this->_creationDate = $this->_reader->readInt64LE(); + $this->_dataPacketsCount = $this->_reader->readInt64LE(); + $this->_playDuration = $this->_reader->readInt64LE(); + $this->_sendDuration = $this->_reader->readInt64LE(); + $this->_preroll = $this->_reader->readInt64LE(); + $this->_flags = $this->_reader->readUInt32LE(); + $this->_minimumDataPacketSize = $this->_reader->readUInt32LE(); + $this->_maximumDataPacketSize = $this->_reader->readUInt32LE(); + $this->_maximumBitrate = $this->_reader->readUInt32LE(); + } + + /** + * Returns the file id field. + * + * @return integer + */ + public function getFileId() { return $this->_fileId; } + + /** + * Returns the size, in bytes, of the entire file. The value of this field is + * invalid if the broadcast flag bit in the flags field is set to 1. + * + * @return integer + */ + public function getFileSize() { return $this->_fileSize; } + + /** + * Returns the date and time of the initial creation of the file. The value is + * given as the number of 100-nanosecond intervals since January 1, 1601, + * according to Coordinated Universal Time (Greenwich Mean Time). The value of + * this field may be invalid if the broadcast flag bit in the flags field is + * set to 1. + * + * @return integer + */ + public function getCreationDate() { return $this->_creationDate; } + + /** + * Returns the number of Data Packet entries that exist within the + * {@link ASF_Object_Data Data Object}. The value of this field is invalid if + * the broadcast flag bit in the flags field is set to 1. + * + * @return integer + */ + public function getDataPacketsCount() { return $this->_dataPacketsCount; } + + /** + * Returns the time needed to play the file in 100-nanosecond units. This + * value should include the duration (estimated, if an exact value is + * unavailable) of the the last media object in the presentation. The value of + * this field is invalid if the broadcast flag bit in the flags field is set + * to 1. + * + * @return integer + */ + public function getPlayDuration() { return $this->_playDuration; } + + /** + * Returns the time needed to send the file in 100-nanosecond units. This + * value should include the duration of the last packet in the content. The + * value of this field is invalid if the broadcast flag bit in the flags field + * is set to 1. + * + * @return integer + */ + public function getSendDuration() { return $this->_sendDuration; } + + /** + * Returns the amount of time to buffer data before starting to play the file, + * in millisecond units. If this value is nonzero, the Play Duration + * field and all of the payload Presentation Time fields have been + * offset by this amount. Therefore, player software must subtract the value + * in the preroll field from the play duration and presentation times to + * calculate their actual values. + * + * @return integer + */ + public function getPreroll() { return $this->_preroll; } + + /** + * Checks whether or not the flag is set. Returns true if the flag + * is set, false otherwise. + * + * @param integer $flag The flag to query. + * @return boolean + */ + public function hasFlag($flag) { return ($this->_flags & $flag) == $flag; } + + /** + * Returns the flags field. + * + * @return integer + */ + public function getFlags() { return $this->_flags; } + + /** + * Returns the minimum Data Packet size in bytes. In general, the value + * of this field is invalid if the broadcast flag bit in the flags field is + * set to 1. However, the values for the Minimum Data Packet Size and + * Maximum Data Packet Size fields shall be set to the same value, and + * this value should be set to the packet size, even when the broadcast flag + * in the flags field is set to 1. + * + * @return integer + */ + public function getMinimumDataPacketSize() + { + return $this->_minimumDataPacketSize; + } + + /** + * Returns the maximum Data Packet size in bytes. In general, the value + * of this field is invalid if the broadcast flag bit in the flags field is + * set to 1. However, the values for the Minimum Data Packet Size and + * Maximum Data Packet Size fields shall be set to the same value, and + * this value should be set to the packet size, even when the broadcast flag + * in the flags field is set to 1. + * + * @return integer + */ + public function getMaximumDataPacketSize() + { + return $this->_maximumDataPacketSize; + } + + /** + * Returns the maximum instantaneous bit rate in bits per second for the + * entire file. This is equal the sum of the bit rates of the individual + * digital media streams. + * + * @return integer + */ + public function getMaximumBitrate() { return $this->_maximumBitrate; } +} diff --git a/src/ASF/Object/Header.php b/src/ASF/Object/Header.php new file mode 100644 index 0000000..3ce7bf2 --- /dev/null +++ b/src/ASF/Object/Header.php @@ -0,0 +1,125 @@ + + * @copyright Copyright (c) 2006-2008 The PHP Reader Project Workgroup + * @license http://code.google.com/p/php-reader/wiki/License New BSD License + * @version $Rev$ + */ +final class ASF_Object_Header extends ASF_Object_Container +{ + const FILE_PROPERTIES = "8cabdca1-a947-11cf-8ee4-00c00c205365"; + const STREAM_PROPERTIES = "b7dc0791-a9b7-11cf-8ee6-00c00c205365"; + const HEADER_EXTENSION = "5fbf03b5-a92e-11cf-8ee3-00c00c205365"; + const CODEC_LIST = "86d15240-311d-11d0-a3a4-00a0c90348f6"; + const SCRIPT_COMMAND = "1efb1a30-0b62-11d0-a39b-00a0c90348f6"; + const MARKER = "f487cd01-a951-11cf-8ee6-00c00c205365"; + const BITRATE_MUTUAL_EXCLUSION = "d6e229dc-35da-11d1-9034-00a0c90349be"; + const ERROR_CORRECTION = "75b22635-668e-11cf-a6d9-00aa0062ce6c"; + const CONTENT_DESCRIPTION = "75b22633-668e-11cf-a6d9-00aa0062ce6c"; + const EXTENDED_CONTENT_DESCRIPTION = "d2d0a440-e307-11d2-97f0-00a0c95ea850"; + const CONTENT_BRANDING = "2211b3fa-bd23-11d2-b4b7-00a0c955fc6e"; + const STREAM_BITRATE_PROPERTIES = "7bf875ce-468d-11d1-8d82-006097c9a2b2"; + const CONTENT_ENCRYPTION = "2211b3fb-bd23-11d2-b4b7-00a0c955fc6e"; + const EXTENDED_CONTENT_ENCRYPTION = "298ae614-2622-4c17-b935-dae07ee9289c"; + const DIGITAL_SIGNATURE = "2211b3fc-bd23-11d2-b4b7-00a0c955fc6e"; + const PADDING = "1806d474-cadf-4509-a4ba-9aabcb96aae8"; + + /** + * Constructs the class with given parameters and options. + * + * @param Reader $reader The reader object. + * @param Array $options The options array. + */ + public function __construct($reader, &$options = array()) + { + parent::__construct($reader, $options); + + $this->_reader->skip(6); + $this->constructObjects + (array + (self::FILE_PROPERTIES => "FileProperties", + self::STREAM_PROPERTIES => "StreamProperties", + self::HEADER_EXTENSION => "HeaderExtension", + self::CODEC_LIST => "CodecList", + self::SCRIPT_COMMAND => "ScriptCommand", + self::MARKER => "Marker", + self::BITRATE_MUTUAL_EXCLUSION => "BitrateMutualExclusion", + self::ERROR_CORRECTION => "ErrorCorrection", + self::CONTENT_DESCRIPTION => "ContentDescription", + self::EXTENDED_CONTENT_DESCRIPTION => "ExtendedContentDescription", + self::CONTENT_BRANDING => "ContentBranding", + self::STREAM_BITRATE_PROPERTIES => "StreamBitrateProperties", + self::CONTENT_ENCRYPTION => "ContentEncryption", + self::EXTENDED_CONTENT_ENCRYPTION => "ExtendedContentEncryption", + self::DIGITAL_SIGNATURE => "DigitalSignature", + self::PADDING => "Padding")); + } +} diff --git a/src/ASF/Object/HeaderExtension.php b/src/ASF/Object/HeaderExtension.php new file mode 100644 index 0000000..169b082 --- /dev/null +++ b/src/ASF/Object/HeaderExtension.php @@ -0,0 +1,101 @@ +Header Extension Object allows additional functionality to be added + * to an ASF file while maintaining backward compatibility. The Header Extension + * Object is a container containing 0 or more additional extended header + * objects. + * + * @package php-reader + * @subpackage ASF + * @author Sven Vollbehr + * @copyright Copyright (c) 2008 The PHP Reader Project Workgroup + * @license http://code.google.com/p/php-reader/wiki/License New BSD License + * @version $Rev$ + */ +final class ASF_Object_HeaderExtension extends ASF_Object_Container +{ + const EXTENDED_STREAM_PROPERTIES = "14e6a5cb-c672-4332-8399-a96952065b5a"; + const ADVANCED_MUTUAL_EXCLUSION = "a08649cf-4775-4670-8a16-6e35357566cd"; + const GROUP_MUTUAL_EXCLUSION = "d1465a40-5a79-4338-b71b-e36b8fd6c249"; + const STREAM_PRIORITIZATION = "d4fed15b-88d3-454f-81f0-ed5c45999e24"; + const BANDWIDTH_SHARING = "a69609e6-517b-11d2-b6af-00c04fd908e9"; + const LANGUAGE_LIST = "7c4346a9-efe0-4bfc-b229-393ede415c85"; + const METADATA = "c5f8cbea-5baf-4877-8467-aa8c44fa4cca"; + const METADATA_LIBRARY = "44231c94-9498-49d1-a141-1d134e457054"; + const INDEX_PARAMETERS = "d6e229df-35da-11d1-9034-00a0c90349be"; + const MEDIA_OBJECT_INDEX_PARAMETERS = "6b203bad-3f11-48e4-aca8-d7613de2cfa7"; + const TIMECODE_INDEX_PARAMETERS = "f55e496d-9797-4b5d-8c8b-604dfe9bfb24"; + const COMPATIBILITY = "75b22630-668e-11cf-a6d9-00aa0062ce6c"; + const ADVANCED_CONTENT_ENCRYPTION = "43058533-6981-49e6-9b74-ad12cb86d58c"; + const PADDING = "1806d474-cadf-4509-a4ba-9aabcb96aae8"; + + /** + * Constructs the class with given parameters and reads object related data + * from the ASF file. + * + * @param Reader $reader The reader object. + * @param Array $options The options array. + */ + public function __construct($reader, &$options = array()) + { + parent::__construct($reader, $options); + + $this->_reader->skip(22); + $this->constructObjects + (array + (self::EXTENDED_STREAM_PROPERTIES => "ExtendedStreamProperties", + self::ADVANCED_MUTUAL_EXCLUSION => "AdvancedMutualExclusion", + self::GROUP_MUTUAL_EXCLUSION => "GroupMutualExclusion", + self::STREAM_PRIORITIZATION => "StreamPrioritization", + self::BANDWIDTH_SHARING => "BandwidthSharing", + self::LANGUAGE_LIST => "LanguageList", + self::METADATA => "Metadata", + self::METADATA_LIBRARY => "MetadataLibrary", + self::INDEX_PARAMETERS => "IndexParameters", + self::MEDIA_OBJECT_INDEX_PARAMETERS => "MediaObjectIndexParameters", + self::TIMECODE_INDEX_PARAMETERS => "TimecodeIndexParameters", + self::COMPATIBILITY => "Compatibility", + self::ADVANCED_CONTENT_ENCRYPTION => "AdvancedContentEncryption", + self::PADDING => "Padding")); + } +} diff --git a/src/ASF/Object/LanguageList.php b/src/ASF/Object/LanguageList.php new file mode 100644 index 0000000..c0195ff --- /dev/null +++ b/src/ASF/Object/LanguageList.php @@ -0,0 +1,85 @@ +Language List Object contains an array of Unicode-based language + * IDs. All other header objects refer to languages through zero-based positions + * in this array. + * + * @package php-reader + * @subpackage ASF + * @author Sven Vollbehr + * @copyright Copyright (c) 2008 The PHP Reader Project Workgroup + * @license http://code.google.com/p/php-reader/wiki/License New BSD License + * @version $Rev$ + */ +final class ASF_Object_LanguageList extends ASF_Object +{ + /** @var Array */ + private $_languages = array(); + + /** + * Constructs the class with given parameters and reads object related data + * from the ASF file. + * + * @param Reader $reader The reader object. + * @param Array $options The options array. + */ + public function __construct($reader, &$options = array()) + { + parent::__construct($reader, $options); + + $languageIdRecordsCount = $this->_reader->readUInt16LE(); + for ($i = 0; $i < $languageIdRecordsCount; $i++) { + $languageIdLength = $this->_reader->readInt8(); + $languageId = $this->_reader->readString16LE($languageIdLength); + $this->_languages[] = iconv + ("utf-16le", $this->getOption("encoding"), $languageId); + } + } + + /** + * Returns the array of language ids. + * + * @return Array + */ + public function getLanguage() { return $this->_languages; } +} diff --git a/src/ASF/Object/Marker.php b/src/ASF/Object/Marker.php new file mode 100644 index 0000000..fd6830e --- /dev/null +++ b/src/ASF/Object/Marker.php @@ -0,0 +1,121 @@ +Marker Object class. + * + * @package php-reader + * @subpackage ASF + * @author Sven Vollbehr + * @copyright Copyright (c) 2008 The PHP Reader Project Workgroup + * @license http://code.google.com/p/php-reader/wiki/License New BSD License + * @version $Rev$ + */ +final class ASF_Object_Marker extends ASF_Object +{ + /** @var string */ + private $_name; + + /** @var Array */ + private $_markers = array(); + + /** + * Constructs the class with given parameters and reads object related data + * from the ASF file. + * + * @param Reader $reader The reader object. + * @param Array $options The options array. + */ + public function __construct($reader, &$options = array()) + { + parent::__construct($reader, $options); + + $this->_reader->skip(16); + $markersCount = $this->_reader->readUInt32LE(); + $this->_reader->skip(2); + $nameLength = $this->_reader->readUInt16LE(); + $this->_name = iconv + ("utf-16le", $this->getOption("encoding"), + $this->_reader->readString16LE($nameLength)); + for ($i = 0; $i < $markersCount; $i++) { + $marker = array + ("offset" => $this->_reader->readInt64LE(), + "presentationTime" => $this->_reader->readInt64LE()); + $this->_reader->skip(2); + $marker["sendTime"] = $this->_reader->readUInt32LE(); + $marker["flags"] = $this->_reader->readUInt32LE(); + $descriptionLength = $this->_reader->readUInt32LE(); + $marker["description"] = iconv + ("utf-16le", $this->getOption("encoding"), + $this->_reader->readString16LE($descriptionLength)); + $this->_markers[] = $marker; + } + } + + /** + * Returns the name of the Marker Object. + * + * @return Array + */ + public function getName() { return $this->_name; } + + /** + * Returns an array of markers. Each entry consists of the following keys. + * + * o offset -- Specifies a byte offset into the Data Object to the + * actual position of the marker in the Data Object. ASF parsers + * must seek to this position to properly display data at the specified + * marker Presentation Time. + * + * o presentationTime -- Specifies the presentation time of the marker, in + * 100-nanosecond units. + * + * o sendTime -- Specifies the send time of the marker entry, in + * milliseconds. + * + * o flags -- Flags are reserved and should be set to 0. + * + * o description -- Specifies a description of the marker entry. + * + * @return Array + */ + public function getMarkers() { return $this->_markers; } +} diff --git a/src/ASF/Object/Metadata.php b/src/ASF/Object/Metadata.php new file mode 100644 index 0000000..066334c --- /dev/null +++ b/src/ASF/Object/Metadata.php @@ -0,0 +1,113 @@ +Metadata Object permits authors to store stream-based metadata in + * a file. This object supports the same types of metadata information as the + * Extended Content Description Object except that it also allows a + * stream number to be specified. + * + * @package php-reader + * @subpackage ASF + * @author Sven Vollbehr + * @copyright Copyright (c) 2008 The PHP Reader Project Workgroup + * @license http://code.google.com/p/php-reader/wiki/License New BSD License + * @version $Rev$ + */ +final class ASF_Object_Metadata extends ASF_Object +{ + /** @var Array */ + private $_descriptions = array(); + + /** + * Constructs the class with given parameters and reads object related data + * from the ASF file. + * + * @param Reader $reader The reader object. + * @param Array $options The options array. + */ + public function __construct($reader, &$options = array()) + { + parent::__construct($reader, $options); + + $descriptionRecordsCount = $this->_reader->readUInt16LE(); + for ($i = 0; $i < $descriptionRecordsCount; $i++) { + $this->_reader->skip(2); + $record = array("streamNumber" => $this->_reader->readUInt16LE()); + $nameLength = $this->_reader->readUInt16LE(); + $dataType = $this->_reader->readUInt16LE(); + $dataLength = $this->_reader->readUInt32LE(); + $record["name"] = iconv + ("utf-16le", $this->getOption("encoding"), + $this->_reader->readString16LE($nameLength)); + switch ($dataType) { + case 0: + $record["data"] = iconv + ("utf-16le", $this->getOption("encoding"), + $this->_reader->readString16LE($dataLength)); + break; + case 1: + $record["data"] = $this->_reader->readString16LE($dataLength); + break; + case 2: + $record["data"] = $this->_reader->readUInt16LE() ? true : false; + break; + case 3: + $record["data"] = $this->_reader->readUInt32LE(); + break; + case 4: + $record["data"] = $this->_reader->readInt64LE(); + break; + case 5: + $record["data"] = $this->_reader->readUInt16LE(); + break; + } + $this->_descriptions[] = $record; + } + } + + /** + * Returns the array of description records. + * + * @return Array + */ + public function getDescriptions() { return $this->_descriptions; } +} diff --git a/src/ASF/Object/MetadataLibrary.php b/src/ASF/Object/MetadataLibrary.php new file mode 100644 index 0000000..930ded0 --- /dev/null +++ b/src/ASF/Object/MetadataLibrary.php @@ -0,0 +1,137 @@ +Metadata Library Object lets authors store stream-based, + * language-attributed, multiply defined, and large metadata attributes in a + * file. + * + * This object supports the same types of metadata as the + * {@link ASF_Object_Metadata Metadata Object}, as well as attributes + * with language IDs, attributes that are defined more than once, large + * attributes, and attributes with the GUID data type. + * + * @package php-reader + * @subpackage ASF + * @author Sven Vollbehr + * @copyright Copyright (c) 2008 The PHP Reader Project Workgroup + * @license http://code.google.com/p/php-reader/wiki/License New BSD License + * @version $Rev$ + */ +final class ASF_Object_MetadataLibrary extends ASF_Object +{ + /** @var Array */ + private $_descriptionRecords = array(); + + /** + * Constructs the class with given parameters and reads object related data + * from the ASF file. + * + * @param Reader $reader The reader object. + * @param Array $options The options array. + */ + public function __construct($reader, &$options = array()) + { + parent::__construct($reader, $options); + + $descriptionRecordsCount = $this->_reader->readUInt16LE(); + for ($i = 0; $i < $descriptionRecordsCount; $i++) { + $descriptionRecord = array + ("languageIndex" => $this->_reader->readUInt16LE(), + "streamNumber" => $this->_reader->readUInt16LE()); + $nameLength = $this->_reader->readUInt16LE(); + $dataType = $this->_reader->readUInt16LE(); + $dataLength = $this->_reader->readUInt32LE(); + $descriptionRecord["name"] = iconv + ("utf-16le", $this->getOption("encoding"), + $this->_reader->readString16LE($nameLength)); + switch ($dataType) { + case 0: // Unicode string + $descriptionRecord["data"] = iconv + ("utf-16le", $this->getOption("encoding"), + $this->_reader->readString16LE($dataLength)); + break; + case 1: // BYTE array + $descriptionRecord["data"] = $this->_reader->read($dataLength); + break; + case 2: // BOOL + $descriptionRecord["data"] = $this->_reader->readUInt16LE() == 1; + break; + case 3: // DWORD + $descriptionRecord["data"] = $this->_reader->readUInt32LE(); + break; + case 4: // QWORD + $descriptionRecord["data"] = $this->_reader->readInt64LE(); + break; + case 5: // WORD + $descriptionRecord["data"] = $this->_reader->readUInt16LE(); + break; + case 6: // GUID + $descriptionRecord["data"] = $this->_reader->readGUID(); + break; + } + $this->_descriptionRecords[] = $descriptionRecord; + } + } + + /** + * Returns an array of description records. Each record consists of the + * following keys. + * + * o languageIndex -- Specifies the index into the Language List + * Object that identifies the language of this attribute. If there is + * no Language List Object present, this field is zero. + * + * o streamNumber -- Specifies whether the entry applies to a specific + * digital media stream or whether it applies to the whole file. A value + * of 0 in this field indicates that it applies to the whole file; + * otherwise, the entry applies only to the indicated stream number. Valid + * values are between 1 and 127. + * + * o name -- Specifies the name that identifies the attribute being + * described. + * + * o data -- Specifies the actual metadata being stored. + * + * @return Array + */ + public function getDescriptionRecords() { return $this->_descriptionRecords; } +} diff --git a/src/ASF/Object/Padding.php b/src/ASF/Object/Padding.php new file mode 100644 index 0000000..8e2a20d --- /dev/null +++ b/src/ASF/Object/Padding.php @@ -0,0 +1,64 @@ +Padding Object is a dummy object that is used to pad the size of + * the Header Object. This object enables the size of any object stored + * in the Header Object to grow or shrink without having to rewrite the + * entire Data Object and Index Object sections of the ASF file. + * For instance, if entries in the Content Description Object or + * Extended Content Description Object need to be removed or shortened, + * the size of the Padding Object can be increased to compensate for the + * reduction in size of the Content Description Object. The ASF file can + * then be updated by overwriting the previous Header Object with the + * edited Header Object of identical size, without having to move or + * rewrite the data contained in the Data Object. + * + * @package php-reader + * @subpackage ASF + * @author Sven Vollbehr + * @copyright Copyright (c) 2008 The PHP Reader Project Workgroup + * @license http://code.google.com/p/php-reader/wiki/License New BSD License + * @version $Rev$ + */ +final class ASF_Object_Padding extends ASF_Object +{ +} diff --git a/src/ASF/Object/ScriptCommand.php b/src/ASF/Object/ScriptCommand.php new file mode 100644 index 0000000..fefe9b8 --- /dev/null +++ b/src/ASF/Object/ScriptCommand.php @@ -0,0 +1,124 @@ +Script Command Object provides a list of type/parameter pairs of + * strings that are synchronized to the ASF file's timeline. Types can include + * URL or FILENAME values. Other type values may also be freely defined and + * used. The semantics and treatment of this set of types are defined by the + * local implementations. The parameter value is specific to the type field. You + * can use this type/parameter pairing for many purposes, including sending URLs + * to be launched by a client into an HTML frame (in other words, the URL type) + * or launching another ASF file for the chained continuous play of audio or + * video presentations (in other words, the FILENAME type). This object is also + * used as a method to stream text, as well as to provide script commands that + * you can use to control elements within the client environment. + * + * @package php-reader + * @subpackage ASF + * @author Sven Vollbehr + * @copyright Copyright (c) 2008 The PHP Reader Project Workgroup + * @license http://code.google.com/p/php-reader/wiki/License New BSD License + * @version $Rev$ + */ +final class ASF_Object_ScriptCommand extends ASF_Object +{ + /** @var Array */ + private $_commandTypes = array(); + + /** @var Array */ + private $_commands = array(); + + /** + * Constructs the class with given parameters and reads object related data + * from the ASF file. + * + * @param Reader $reader The reader object. + * @param Array $options The options array. + */ + public function __construct($reader, &$options = array()) + { + parent::__construct($reader, $options); + + $this->_reader->skip(16); + $commandsCount = $this->_reader->readUInt16LE(); + $commandTypesCount = $this->_reader->readUInt16LE(); + for ($i = 0; $i < $commandTypesCount; $i++) { + $commandTypeNameLength = $this->_reader->readUInt16LE(); + $this->_commandTypes[] = iconv + ("utf-16le", $this->getOption("encoding"), + $this->_reader->readString16LE($commandTypeNameLength * 2)); + } + for ($i = 0; $i < $commandsCount; $i++) { + $command = array + ("presentationTime" => $this->_reader->readUInt32LE(), + "typeIndex" => $this->_reader->readUInt16LE()); + $commandNameLength = $this->_reader->readUInt16LE(); + $command["name"] = iconv + ("utf-16le", $this->getOption("encoding"), + $this->_reader->readString16LE($commandNameLength * 2)); + $this->_commands[] = $command; + } + } + + /** + * Returns an array of command type names. + * + * @return Array + */ + public function getCommandTypes() { return $this->_commandTypes; } + + /** + * Returns an array of index entries. Each entry consists of the following + * keys. + * + * o presentationTime -- Specifies the presentation time of the command, in + * milliseconds. + * + * o typeIndex -- Specifies the type of this command, as a zero-based index + * into the array of Command Types of this object. + * + * o name -- Specifies the name of this command. + * + * @return Array + */ + public function getCommands() { return $this->_commands; } +} diff --git a/src/ASF/Object/SimpleIndex.php b/src/ASF/Object/SimpleIndex.php new file mode 100644 index 0000000..b7987fc --- /dev/null +++ b/src/ASF/Object/SimpleIndex.php @@ -0,0 +1,143 @@ +Simple Index Object. Additionally, the instances of the Simple + * Index Object shall be ordered by stream number. + * + * Index entries in the Simple Index Object are in terms of + * Presentation Times. The corresponding Packet Number field + * values (of the Index Entry, see below) indicate the packet number of + * the ASF Data Packet with the closest past key frame. Note that for + * video streams that contain both key frames and non-key frames, the Packet + * Number field will always point to the closest past key frame. + * + * @package php-reader + * @subpackage ASF + * @author Sven Vollbehr + * @copyright Copyright (c) 2008 The PHP Reader Project Workgroup + * @license http://code.google.com/p/php-reader/wiki/License New BSD License + * @version $Rev$ + */ +final class ASF_Object_SimpleIndex extends ASF_Object +{ + /** @var string */ + private $_fileId; + + /** @var integer */ + private $_indexEntryTimeInterval; + + /** @var integer */ + private $_maximumPacketCount; + + /** @var Array */ + private $_indexEntries = array(); + + /** + * Constructs the class with given parameters and reads object related data + * from the ASF file. + * + * @param Reader $reader The reader object. + * @param Array $options The options array. + */ + public function __construct($reader, &$options = array()) + { + parent::__construct($reader, $options); + + $this->_fileId = $this->_reader->readGUID(); + $this->_indexEntryTimeInterval = $this->_reader->readInt64LE(); + $this->_maximumPacketCount = $this->_reader->readUInt32LE(); + $indexEntriesCount = $this->_reader->readUInt32LE(); + for ($i = 1; $i < $indexEntriesCount; $i++) { + $this->_indexEntries[] = array + ("packetNumber" => $this->_reader->readUInt32LE(), + "packetCount" => $this->_reader->readUInt16LE()); + } + } + + /** + * Returns the unique identifier for this ASF file. The value of this field + * should be changed every time the file is modified in any way. The value of + * this field may be set to 0 or set to be identical to the value of the + * File ID field of the Data Object and the Header + * Object. + * + * @return string + */ + public function getFileId() { return $this->_fileId; } + + /** + * Returns the time interval between each index entry in 100-nanosecond units. + * The most common value is 10000000, to indicate that the index entries are + * in 1-second intervals, though other values can be used as well. + * + * @return integer + */ + public function getIndexEntryTimeInterval() + { + return $this->_indexEntryTimeInterval; + } + + /** + * Returns the maximum Packet Count value of all Index Entries. + * + * @return integer + */ + public function getMaximumPacketCount() { return $this->_maximumPacketCount; } + + /** + * Returns an array of index entries. Each entry consists of the following + * keys. + * + * o packetNumber -- Specifies the number of the Data Packet associated + * with this index entry. Note that for video streams that contain both + * key frames and non-key frames, this field will always point to the + * closest key frame prior to the time interval. + * + * o packetCount -- Specifies the number of Data Packets to send at this + * index entry. If a video key frame has been fragmented into two Data + * Packets, the value of this field will be equal to 2. + * + * @return Array + */ + public function getIndexEntries() { return $this->_indexEntries; } +} diff --git a/src/ASF/Object/StreamBitrateProperties.php b/src/ASF/Object/StreamBitrateProperties.php new file mode 100644 index 0000000..199d14f --- /dev/null +++ b/src/ASF/Object/StreamBitrateProperties.php @@ -0,0 +1,93 @@ +Stream Bitrate Properties Object defines the average bit rate of + * each digital media stream. + * + * @package php-reader + * @subpackage ASF + * @author Sven Vollbehr + * @copyright Copyright (c) 2008 The PHP Reader Project Workgroup + * @license http://code.google.com/p/php-reader/wiki/License New BSD License + * @version $Rev$ + */ +final class ASF_Object_StreamBitrateProperties extends ASF_Object +{ + /** @var Array */ + private $_bitrateRecords = array(); + + /** + * Constructs the class with given parameters and reads object related data + * from the ASF file. + * + * @param Reader $reader The reader object. + * @param Array $options The options array. + */ + public function __construct($reader, &$options = array()) + { + parent::__construct($reader, $options); + + $bitrateRecordsCount = $this->_reader->readUInt16LE(); + for ($i = 0; $i < $bitrateRecordsCount; $i++) + $this->_bitrateRecords[] = array + ("streamNumber" => ($tmp = $this->_reader->readInt16LE()) & 0x1f, + "flags" => $tmp >> 5, + "averageBitrate" => $this->_reader->readUInt32LE()); + } + + /** + * Returns an array of bitrate records. Each record consists of the following + * keys. + * + * o streamNumber -- Specifies the number of this stream described by this + * record. 0 is an invalid stream. Valid values are between 1 and 127. + * + * o flags -- These bits are reserved and should be set to 0. + * + * o averageBitrate -- Specifies the average bit rate of the stream in bits + * per second. This value should include an estimate of ASF packet and + * payload overhead associated with this stream. + * + * @return Array + */ + public function getBitrateRecords() { return $this->_bitrateRecords; } +} diff --git a/src/ASF/Object/StreamProperties.php b/src/ASF/Object/StreamProperties.php new file mode 100644 index 0000000..46e3a66 --- /dev/null +++ b/src/ASF/Object/StreamProperties.php @@ -0,0 +1,288 @@ +Stream Properties Object defines the specific properties and + * characteristics of a digital media stream. This object defines how a digital + * media stream within the Data Object is interpreted, as well as the + * specific format (of elements) of the Data Packet itself. + * + * Whereas every stream in an ASF presentation, including each stream in a + * mutual exclusion relationship, must be represented by a Stream Properties + * Object, in certain cases, this object might be found embedded in the + * Extended Stream Properties Object. + * + * @package php-reader + * @subpackage ASF + * @author Sven Vollbehr + * @copyright Copyright (c) 2008 The PHP Reader Project Workgroup + * @license http://code.google.com/p/php-reader/wiki/License New BSD License + * @version $Rev$ + */ +final class ASF_Object_StreamProperties extends ASF_Object +{ + /** + * Indicates, if set, that the data contained in this stream is encrypted and + * will be unreadable unless there is a way to decrypt the stream. + */ + const ENCRYPTED_CONTENT = 0x8000; + + const AUDIO_MEDIA = "f8699e40-5b4d-11cf-a8fd-00805f5c442b"; + const VIDEO_MEDIA = "bc19efc0-5b4d-11cf-a8fd-00805f5c442b"; + const COMMAND_MEDIA = "59dacfc0-59e6-11d0-a3ac-00a0c90348f6"; + const JFIF_MEDIA = "b61be100-5b4e-11cf-a8fD-00805f5c442b"; + const DEGRADABLE_JPEG_MEDIA = "35907dE0-e415-11cf-a917-00805f5c442b"; + const FILE_TRANSFER_MEDIA = "91bd222c-f21c-497a-8b6d-5aa86bfc0185"; + const BINARY_MEDIA = "3afb65e2-47ef-40f2-ac2c-70a90d71d343"; + + const NO_ERROR_CORRECTION = "20fb5700-5b55-11cf-a8fd-00805f5c442b"; + const AUDIO_SPREAD = "bfc3cd50-618f-11cf-8bb2-00aa00b4e220"; + + /** @var string */ + private $_streamType; + + /** @var string */ + private $_errorCorrectionType; + + /** @var integer */ + private $_timeOffset; + + /** @var integer */ + private $_flags; + + /** @var Array */ + private $_typeSpecificData = array(); + + /** @var Array */ + private $_errorCorrectionData = array(); + + /** + * Constructs the class with given parameters and reads object related data + * from the ASF file. + * + * @param Reader $reader The reader object. + * @param Array $options The options array. + */ + public function __construct($reader, &$options = array()) + { + parent::__construct($reader, $options); + + $this->_streamType = $this->_reader->readGUID(); + $this->_errorCorrectionType = $this->_reader->readGUID(); + $this->_timeOffset = $this->_reader->readInt64LE(); + $typeSpecificDataLength = $this->_reader->readUInt32LE(); + $errorCorrectionDataLength = $this->_reader->readUInt32LE(); + $this->_flags = $this->_reader->readUInt16LE(); + $this->_reader->skip(4); + switch ($this->_streamType) { + case self::AUDIO_MEDIA: + $this->_typeSpecificData = array + ("codecId" => $this->_reader->readUInt16LE(), + "numberOfChannels" => $this->_reader->readUInt16LE(), + "samplesPerSecond" => $this->_reader->readUInt32LE(), + "avgNumBytesPerSecond" => $this->_reader->readUInt32LE(), + "blockAlignment" => $this->_reader->readUInt16LE(), + "bitsPerSample" => $this->_reader->readUInt16LE()); + $codecSpecificDataSize = $this->_reader->readUInt16LE(); + $this->_typeSpecificData["codecSpecificData"] = + $this->_reader->read($codecSpecificDataSize); + break; + case self::VIDEO_MEDIA: + $this->_typeSpecificData = array + ("encodedImageWidth" => $this->_reader->readUInt32LE(), + "encodedImageHeight" => $this->_reader->readUInt32LE(), + "reservedFlags" => $this->_reader->readInt8()); + $this->_reader->skip(2); + $formatDataSize = $this->_reader->readUInt32LE(); + $this->_typeSpecificData = array_merge + ($this->_typeSpecificData, array + ("imageWidth" => $this->_reader->readUInt32LE(), + "imageHeight" => $this->_reader->readUInt32LE(), + "reserved" => $this->_reader->readUInt16LE(), + "bitsPerPixelCount" => $this->_reader->readUInt16LE(), + "compressionId" => $this->_reader->readUInt32LE(), + "imageSize" => $this->_reader->readUInt32LE(), + "horizontalPixelsPerMeter" => $this->_reader->readUInt32LE(), + "verticalPixelsPerMeter" => $this->_reader->readUInt32LE(), + "colorsUsedCount" => $this->_reader->readUInt32LE(), + "importantColorsCount" => $this->_reader->readUInt32LE(), + "codecSpecificData" => $this->_reader->read($formatDataSize - 38))); + break; + case self::JFIF_MEDIA: + $this->_typeSpecificData = array + ("imageWidth" => $this->_reader->readUInt32LE(), + "imageHeight" => $this->_reader->readUInt32LE(), + "reserved" => $this->_reader->readUInt32LE()); + break; + case self::DEGRADABLE_JPEG_MEDIA: + $this->_typeSpecificData = array + ("imageWidth" => $this->_reader->readUInt32LE(), + "imageHeight" => $this->_reader->readUInt32LE(), + $this->_reader->readUInt16LE(), + $this->_reader->readUInt16LE(), + $this->_reader->readUInt16LE()); + $interchangeDataSize = $this->_reader->readUInt16LE(); + if ($interchangeDataSize == 0) + $interchangeDataSize++; + $this->_typeSpecificData["interchangeData"] = + $this->_reader->read($interchangeDataSize); + break; + case self::FILE_TRANSFER_MEDIA: + case self::BINARY_MEDIA: + $this->_typeSpecificData = array + ("majorMediaType" => $this->_reader->getGUID(), + "mediaSubtype" => $this->_reader->getGUID(), + "fixedSizeSamples" => $this->_reader->readUInt32LE(), + "temporalCompression" => $this->_reader->readUInt32LE(), + "sampleSize" => $this->_reader->readUInt32LE(), + "formatType" => $this->_reader->getGUID()); + $formatDataSize = $this->_reader->readUInt32LE(); + $this->_typeSpecificData["formatData"] = + $this->_reader->read($formatDataSize); + break; + case self::COMMAND_MEDIA: + default: + $this->_reader->skip($typeSpecificDataLength); + } + switch ($this->_errorCorrectionType) { + case self::AUDIO_SPREAD: + $this->_errorCorrectionData = array + ("span" => $this->_reader->readInt8(), + "virtualPacketLength" => $this->_reader->readUInt16LE(), + "virtualChunkLength" => $this->_reader->readUInt16LE()); + $silenceDataSize = $this->_reader->readUInt16LE(); + $this->_errorCorrectionData["silenceData"] = + $this->_reader->read($silenceDataSize); + break; + case self::NO_ERROR_CORRECTION: + default: + $this->_reader->skip($errorCorrectionDataLength); + } + } + + /** + * Returns the number of this stream. 0 is an invalid stream. Valid values are + * between 1 and 127. The numbers assigned to streams in an ASF presentation + * may be any combination of unique values; parsing logic must not assume that + * streams are numbered sequentially. + * + * @return integer + */ + public function getStreamNumber() { return $this->_flags & 0x3f; } + + /** + * Returns the type of the stream (for example, audio, video, and so on). + * + * @return string + */ + public function getStreamType() { return $this->_streamType; } + + /** + * Returns the error correction type used by this digital media stream. For + * streams other than audio, this value should be set to NO_ERROR_CORRECTION. + * For audio streams, this value should be set to AUDIO_SPREAD. + * + * @return string + */ + public function getErrorCorrectionType() + { + return $this->_errorCorrectionType; + } + + /** + * Returns the presentation time offset of the stream in 100-nanosecond units. + * The value of this field is added to all of the timestamps of the samples in + * the stream. This value shall be equal to the send time of the first + * interleaved packet in the data section. The value of this field is + * typically 0. It is non-zero in the case when an ASF file is edited and it + * is not possible for the editor to change the presentation times and send + * times of ASF packets. Note that if more than one stream is present in an + * ASF file the offset values of all stream properties objects must be equal. + * + * @return integer + */ + public function getTimeOffset() { return $this->_timeOffset; } + + /** + * Checks whether or not the flag is set. Returns true if the flag + * is set, false otherwise. + * + * @param integer $flag The flag to query. + * @return boolean + */ + public function hasFlag($flag) { return ($this->_flags & $flag) == $flag; } + + /** + * Returns the flags field. + * + * @return integer + */ + public function getFlags() { return $this->_flags; } + + /** + * Returns type-specific format data. The structure for the Type-Specific + * Data field is determined by the value stored in the Stream Type + * field. + * + * The type-specific data is returned as key-value pairs of an associate + * array. + * + * @return Array + */ + public function getTypeSpecificData() { return $this->_typeSpecificData; } + + /** + * Returns data specific to the error correction type. The structure for the + * Error Correction Data field is determined by the value stored in the + * Error Correction Type field. For example, an audio data stream might + * need to know how codec chunks were redistributed, or it might need a sample + * of encoded silence. + * + * The error correction type-specific data is returned as key-value pairs of + * an associate array. + * + * @return integer + */ + public function getErrorCorrectionData() + { + return $this->_errorCorrectionData; + } +} diff --git a/src/ISO14496/Box.php b/src/ISO14496/Box.php index a78992c..d8ecd9c 100644 --- a/src/ISO14496/Box.php +++ b/src/ISO14496/Box.php @@ -85,9 +85,10 @@ class ISO14496_Box private static $_path = array(); /** - * Constructs the class with given parameters. + * Constructs the class with given parameters and options. * - * @param Reader $reader The reader object. + * @param Reader $reader The reader object. + * @param Array $options The options array. */ public function __construct($reader, &$options = array()) { @@ -176,7 +177,6 @@ class ISO14496_Box * * The method will propagate size change to box parents. * - * @todo Size does not propagate to parent * @param integer $size The box size. */ public function setSize($size) @@ -397,8 +397,7 @@ class ISO14496_Box public function __set($name, $value) { if (method_exists($this, "set" . ucfirst($name))) - call_user_func - (array($this, "set" . ucfirst($name)), $value); + call_user_func(array($this, "set" . ucfirst($name)), $value); else throw new ISO14496_Exception("Unknown field: " . $name); } diff --git a/src/Transform.php b/src/Transform.php index 143a4e6..56bb3c2 100644 --- a/src/Transform.php +++ b/src/Transform.php @@ -471,7 +471,8 @@ final class Transform public static function fromString16LE($value) { $string = ""; - foreach (unpack("v*", $value) as $char) + foreach (unpack("v*", substr($value, -2) == "\0\0" ? + substr($value, 0, -2) : $value) as $char) $string .= pack("S", $char); return $string; } @@ -496,7 +497,8 @@ final class Transform public static function fromString16BE($value) { $string = ""; - foreach (unpack("n*", $value) as $char) + foreach (unpack("n*", substr($value, -2) == "\0\0" ? + substr($value, 0, -2) : $value) as $char) $string .= pack("S", $char); return $string; }