/** * Decode the stream header * @access private */ function _decodeHeader() { fseek($this->_filePointer, $this->_streamList[0]['body_offset'], SEEK_SET); // The first 8 characters should be "Speex ". if (fread($this->_filePointer, 8) != 'Speex ') { throw new PEAR_Exception("Stream is undecodable due to a malformed header.", OGG_ERROR_UNDECODABLE); } $this->_version = fread($this->_filePointer, 20); $this->_header = File_Ogg::_readLittleEndian($this->_filePointer, array('speex_version_id' => 32, 'header_size' => 32, 'rate' => 32, 'mode' => 32, 'mode_bitstream_version' => 32, 'nb_channels' => 32, 'bitrate' => 32, 'frame_size' => 32, 'vbr' => 32, 'frames_per_packet' => 32, 'extra_headers' => 32, 'reserved1' => 32, 'reserved2' => 32)); $this->_header['speex_version'] = $this->_version; }
function getMetadata( $image, $path ) { $metadata = array( 'version' => self::METADATA_VERSION ); if ( !class_exists( 'File_Ogg' ) ) { require( 'File/Ogg.php' ); } try { $f = new File_Ogg( $path ); $streams = array(); foreach ( $f->listStreams() as $streamIDs ) { foreach ( $streamIDs as $streamID ) { $stream = $f->getStream( $streamID ); $streams[$streamID] = array( 'serial' => $stream->getSerial(), 'group' => $stream->getGroup(), 'type' => $stream->getType(), 'vendor' => $stream->getVendor(), 'length' => $stream->getLength(), 'size' => $stream->getSize(), 'header' => $stream->getHeader(), 'comments' => $stream->getComments() ); } } $metadata['streams'] = $streams; $metadata['length'] = $f->getLength(); // Get the offset of the file (in cases where the file is a segment copy) $metadata['offset'] = $f->getStartOffset(); } catch ( PEAR_Exception $e ) { // File not found, invalid stream, etc. $metadata['error'] = array( 'message' => $e->getMessage(), 'code' => $e->getCode() ); } return serialize( $metadata ); }
/** * Parse the identification header (the first of three headers) in a Vorbis stream. * * This function parses the identification header. The identification header * contains simple audio characteristics, such as sample rate and number of * channels. There are a number of error-checking provisions laid down in the Vorbis * specification to ensure the stream is pure. * * @access private */ function _decodeIdentificationHeader() { $this->_decodeCommonHeader(OGG_VORBIS_IDENTIFICATION_HEADER, OGG_VORBIS_IDENTIFICATION_PAGE_OFFSET); $h = File_Ogg::_readLittleEndian($this->_filePointer, array('vorbis_version' => 32, 'audio_channels' => 8, 'audio_sample_rate' => 32, 'bitrate_maximum' => 32, 'bitrate_nominal' => 32, 'bitrate_minimum' => 32, 'blocksize_0' => 4, 'blocksize_1' => 4, 'framing_flag' => 1)); // The Vorbis stream version must be 0. if ($h['vorbis_version'] == 0) { $this->_version = $h['vorbis_version']; } else { throw new PEAR_Exception("Stream is undecodable due to an invalid vorbis stream version.", OGG_VORBIS_ERROR_UNDECODABLE); } // The number of channels MUST be greater than 0. if ($h['audio_channels'] == 0) { throw new PEAR_Exception("Stream is undecodable due to zero channels.", OGG_VORBIS_ERROR_UNDECODABLE); } else { $this->_channels = $h['audio_channels']; } // The sample rate MUST be greater than 0. if ($h['audio_sample_rate'] == 0) { throw new PEAR_Exception("Stream is undecodable due to a zero sample rate.", OGG_VORBIS_ERROR_UNDECODABLE); } else { $this->_sampleRate = $h['audio_sample_rate']; } // Extract the various bitrates $this->_maxBitrate = $h['bitrate_maximum']; $this->_nomBitrate = $h['bitrate_nominal']; $this->_minBitrate = $h['bitrate_minimum']; // Powers of two between 6 and 13 inclusive. $valid_block_sizes = array(64, 128, 256, 512, 1024, 2048, 4096, 8192); // blocksize_0 MUST be a valid blocksize. $blocksize_0 = pow(2, $h['blocksize_0']); if (FALSE == in_array($blocksize_0, $valid_block_sizes)) { throw new PEAR_Exception("Stream is undecodable because blocksize_0 is {$blocksize_0}, which is not a valid size.", OGG_VORBIS_ERROR_UNDECODABLE); } // Extract bits 5 to 8 from the character data. // blocksize_1 MUST be a valid blocksize. $blocksize_1 = pow(2, $h['blocksize_1']); if (FALSE == in_array($blocksize_1, $valid_block_sizes)) { throw new PEAR_Exception("Stream is undecodable because blocksize_1 is not a valid size.", OGG_VORBIS_ERROR_UNDECODABLE); } // blocksize 0 MUST be less than or equal to blocksize 1. if ($blocksize_0 > $blocksize_1) { throw new PEAR_Exception("Stream is undecodable because blocksize_0 is not less than or equal to blocksize_1.", OGG_VORBIS_ERROR_UNDECODABLE); } // The framing bit MUST be set to mark the end of the identification header. // Some encoders are broken though -- TS /* if ($h['framing_flag'] == 0) throw new PEAR_Exception("Stream in undecodable because the framing bit is not non-zero.", OGG_VORBIS_ERROR_UNDECODABLE); */ $this->_idHeader = $h; }
function _decodeCommentsHeader() { fseek($this->_filePointer, $this->_streamData['pages'][1]['body_offset'], SEEK_SET); $blockHeader = File_Ogg::_readBigEndian($this->_filePointer, array('last_block' => 1, 'block_type' => 7, 'length' => 24)); if ($blockHeader['block_type'] != 4) { throw new PEAR_Exception("Stream Undecodable", OGG_ERROR_UNDECODABLE); } $this->_decodeBareCommentsHeader(); }
/** * Parse the identification header in a Theora stream. * @access private */ function _decodeIdentificationHeader() { $this->_decodeCommonHeader(OGG_THEORA_IDENTIFICATION_HEADER, OGG_THEORA_IDENTIFICATION_PAGE_OFFSET); $h = File_Ogg::_readBigEndian($this->_filePointer, array('VMAJ' => 8, 'VMIN' => 8, 'VREV' => 8, 'FMBW' => 16, 'FMBH' => 16, 'PICW' => 24, 'PICH' => 24, 'PICX' => 8, 'PICY' => 8, 'FRN' => 32, 'FRD' => 32, 'PARN' => 24, 'PARD' => 24, 'CS' => 8, 'NOMBR' => 24, 'QUAL' => 6, 'KFGSHIFT' => 5, 'PF' => 2)); if (!$h) { throw new PEAR_Exception("Stream is undecodable due to a truncated header.", OGG_ERROR_UNDECODABLE); } // Theora version // Seems overly strict but this is what the spec says // VREV is for backwards-compatible changes, apparently if ($h['VMAJ'] != 3 || $h['VMIN'] != 2) { throw new PEAR_Exception("Stream is undecodable due to an invalid theora version.", OGG_ERROR_UNDECODABLE); } $this->_theoraVersion = "{$h['VMAJ']}.{$h['VMIN']}.{$h['VREV']}"; // Frame height/width if (!$h['FMBW'] || !$h['FMBH']) { throw new PEAR_Exception("Stream is undecodable because it has frame size of zero.", OGG_ERROR_UNDECODABLE); } $this->_frameWidth = $h['FMBW'] * 16; $this->_frameHeight = $h['FMBH'] * 16; // Picture height/width if ($h['PICW'] > $this->_frameWidth || $h['PICH'] > $this->_frameHeight) { throw new PEAR_Exception("Stream is undecodable because the picture width is greater than the frame width.", OGG_ERROR_UNDECODABLE); } $this->_pictureWidth = $h['PICW']; $this->_pictureHeight = $h['PICH']; // Picture offset $this->_offsetX = $h['PICX']; $this->_offsetY = $h['PICY']; // Frame rate $this->_frameRate = $h['FRD'] == 0 ? 0 : $h['FRN'] / $h['FRD']; // Physical aspect ratio if (!$h['PARN'] || !$h['PARD']) { $this->_physicalAspectRatio = 1; } else { $this->_physicalAspectRatio = $h['PARN'] / $h['PARD']; } // Color space $colorSpaces = array(0 => 'Undefined', 1 => 'Rec. 470M', 2 => 'Rec. 470BG'); if (isset($colorSpaces[$h['CS']])) { $this->_colorSpace = $colorSpaces[$h['CS']]; } else { $this->_colorSpace = 'Unknown (reserved)'; } $this->_nomBitrate = $h['NOMBR']; $this->_quality = $h['QUAL']; $this->_kfgShift = $h['KFGSHIFT']; $pixelFormats = array(0 => '4:2:0', 1 => 'Unknown (reserved)', 2 => '4:2:2', 3 => '4:4:4'); $this->_pixelFormat = $pixelFormats[$h['PF']]; switch ($h['PF']) { case 0: $h['NSBS'] = floor(($h['FMBW'] + 1) / 2) * floor(($h['FMBH'] + 1) / 2) + 2 * floor(($h['FMBW'] + 3) / 4) * floor(($h['FMBH'] + 3) / 4); $h['NBS'] = 6 * $h['FMBW'] * $h['FMBH']; break; case 2: $h['NSBS'] = floor(($h['FMBW'] + 1) / 2) * floor(($h['FMBH'] + 1) / 2) + 2 * floor(($h['FMBW'] + 3) / 4) * floor(($h['FMBH'] + 1) / 2); $h['NBS'] = 8 * $h['FMBW'] * $h['FMBH']; break; case 3: $h['NSBS'] = 3 * floor(($h['FMBW'] + 1) / 2) * floor(($h['FMBH'] + 1) / 2); $h['NBS'] = 12 * $h['FMBW'] * $h['FMBH']; break; default: $h['NSBS'] = $h['NBS'] = 0; } $h['NMBS'] = $h['FMBW'] * $h['FMBH']; $this->_idHeader = $h; }
function loadMeta() { //load from the file: if (is_file($this->oggPath . OGGCHOP_META_EXT)) { $oggMeta = file_get_contents($this->oggPath . OGGCHOP_META_EXT); //check if a separate request is working on generating the file: if (trim($oggMeta) == 'loading') { if ($this->loadWaitCount >= 24) { //we have waited 2 min with no luck.. //@@todo we should flag that ogg file as broken? // and just redirect to normal output? (for now just set meta to false) $this->meta = false; //fail: return false; } else { //some other request is "loading" metadata sleep for 5 seconds and try again sleep(5); $this->loadWaitCount++; return $this->loadMeta(); } } else { $this->meta = unserialize($oggMeta); if ($this->meta['version'] == 'OGGCHOP_META_VERSION') { //we have a good version of the metadata return true: return true; } else { $this->meta = false; } } } //if the file does not exist or $this->meta is still false:: if (!is_file($this->oggPath . OGGCHOP_META_EXT) || $this->meta === false) { //set the meta file to "loading" (avoids multiple indexing requests) file_put_contents($this->oggPath . OGGCHOP_META_EXT, 'loading'); //load up the File/Ogg Pear module if (!class_exists('File_Ogg')) { require 'File/Ogg.php'; } $f = new File_Ogg($this->oggPath); $streams = array(); $this->meta = array('version' => OGGCHOP_META_VERSION); foreach ($f->listStreams() as $streamType => $streamIDs) { foreach ($streamIDs as $streamID) { $stream = $f->getStream($streamID); //for now only support a fist theora stream we find: if (strtolower($stream->getType()) == 'theora') { $this->meta['theoraKeyFrameInx'] = $stream->getKeyFrameIndex(); //set the width and height: $head = $stream->getHeader(); $this->meta['width'] = $head['PICW']; $this->meta['height'] = $head['PICH']; break; } /* more detailed per-stream metadata:: * $this->meta['streams'][$streamID] = array( 'serial' => $stream->getSerial(), 'group' => $stream->getGroup(), 'type' => $stream->getType(), 'vendor' => $stream->getVendor(), 'length' => $stream->getLength(), 'size' => $stream->getSize(), 'header' => $stream->getHeader(), 'comments' => $stream->getComments() );*/ } } $this->meta['duration'] = $f->getLength(); //cache the metadata:: file_put_contents($this->oggPath . OGGCHOP_META_EXT, serialize($this->meta)); return true; } }