diff options
Diffstat (limited to 'extensions/TimedMediaHandler/handlers/OggHandler/OggHandler.php')
-rw-r--r-- | extensions/TimedMediaHandler/handlers/OggHandler/OggHandler.php | 379 |
1 files changed, 379 insertions, 0 deletions
diff --git a/extensions/TimedMediaHandler/handlers/OggHandler/OggHandler.php b/extensions/TimedMediaHandler/handlers/OggHandler/OggHandler.php new file mode 100644 index 00000000..83e1a0e7 --- /dev/null +++ b/extensions/TimedMediaHandler/handlers/OggHandler/OggHandler.php @@ -0,0 +1,379 @@ +<?php +/** + * ogg handler + */ +class OggHandlerTMH extends TimedMediaHandler { + const METADATA_VERSION = 2; + + /** + * @param $image File + * @param $path string + * @return string + */ + function getMetadata( $image, $path ) { + $metadata = array( 'version' => self::METADATA_VERSION ); + + 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 ( OggException $e ) { + // File not found, invalid stream, etc. + $metadata['error'] = array( + 'message' => $e->getMessage(), + 'code' => $e->getCode() + ); + } + return serialize( $metadata ); + } + + /** + * Display metadata box on file description page. + * + * This is pretty basic, it puts data from all the streams together, + * and only outputs a couple of the most commonly used ogg "comments", + * with comments from all the streams combined + * + * @param File $file + * @param bool|IContextSource $context Context to use (optional) + * @return array|bool + */ + public function formatMetadata( $file, $context = false ) { + $meta = $this->getCommonMetaArray( $file ); + if ( count( $meta ) === 0 ) { + return false; + } + return $this->formatMetadataHelper( $meta, $context ); + } + + /** + * Get some basic metadata properties that are common across file types. + * + * @param File $file + * @return array Array of metadata. See MW's FormatMetadata class for format. + */ + public function getCommonMetaArray( File $file ) { + $metadata = $this->unpackMetadata( $file->getMetadata() ); + if ( !$metadata || isset( $metadata['error'] ) || !isset( $metadata['streams'] ) ) { + return array(); + } + + // See http://www.xiph.org/vorbis/doc/v-comment.html + // http://age.hobba.nl/audio/mirroredpages/ogg-tagging.html + $metadataMap = array( + 'title' => 'ObjectName', + 'artist' => 'Artist', + 'performer' => 'Artist', + 'description' => 'ImageDescription', + 'license' => 'UsageTerms', + 'copyright' => 'Copyright', + 'organization' => 'dc-publisher', + 'date' => 'DateTimeDigitized', + 'location' => 'LocationDest', + 'contact' => 'Contact', + 'encoded_using' => 'Software', + 'encoder' => 'Software', + // OpenSubtitles.org hash. Identifies source video. + 'source_ohash' => 'OriginalDocumentID', + 'comment' => 'UserComment', + 'language' => 'LanguageCode', + ); + + $props = array(); + + foreach( $metadata['streams'] as $stream ) { + if ( isset( $stream['vendor'] ) ) { + if ( !isset( $props['Software'] ) ) { + $props['Software'] = array(); + } + $props['Software'][] = trim( $stream['vendor'] ); + } + if ( !isset( $stream['comments'] ) ) { + continue; + } + foreach( $stream['comments'] as $name => $rawValue ) { + // $value will be an array if the file has + // a multiple tags with the same name. Otherwise it + // is a string. + foreach( (array) $rawValue as $value ) { + $trimmedValue = trim( $value ); + if ( $trimmedValue === '' ) { + continue; + } + $lowerName = strtolower( $name ); + if ( isset( $metadataMap[$lowerName] ) ) { + $convertedName = $metadataMap[$lowerName]; + if ( !isset( $props[$convertedName] ) ) { + $props[$convertedName] = array(); + } + $props[$convertedName][] = $trimmedValue; + } + } + } + + } + // properties might be duplicated across streams + foreach( $props as &$type ) { + $type = array_unique( $type ); + $type = array_values( $type ); + } + + return $props; + } + + /** + * Get the "media size" + * + * @param $file File + * @param $path string + * @param $metadata bool + * @return array|bool + */ + function getImageSize( $file, $path, $metadata = false ) { + global $wgMediaVideoTypes; + // Just return the size of the first video stream + if ( $metadata === false ) { + $metadata = $file->getMetadata(); + } + $metadata = $this->unpackMetadata( $metadata ); + if ( isset( $metadata['error'] ) || !isset( $metadata['streams'] ) ) { + return false; + } + foreach ( $metadata['streams'] as $stream ) { + if ( in_array( $stream['type'], $wgMediaVideoTypes ) ) { + $pictureWidth = $stream['header']['PICW']; + $parNumerator = $stream['header']['PARN']; + $parDenominator = $stream['header']['PARD']; + if( $parNumerator && $parDenominator ) { + // Compensate for non-square pixel aspect ratios + $pictureWidth = $pictureWidth * $parNumerator / $parDenominator; + } + return array( + intval( $pictureWidth ), + intval( $stream['header']['PICH'] ) + ); + } + } + return array( false, false ); + } + + /** + * @param $metadata + * @return bool|mixed + */ + function unpackMetadata( $metadata ) { + wfSuppressWarnings(); + $unser = unserialize( $metadata ); + wfRestoreWarnings(); + if ( isset( $unser['version'] ) && $unser['version'] == self::METADATA_VERSION ) { + return $unser; + } else { + return false; + } + } + + /** + * @param $image + * @return string + */ + function getMetadataType( $image ) { + return 'ogg'; + } + /** + * @param $file File + */ + function getWebType( $file ) { + $baseType = ( $file->getWidth() == 0 && $file->getHeight() == 0 )? 'audio' : 'video'; + $baseType .= '/ogg'; + $streamTypes = $this->getStreamTypes( $file ); + if ( !$streamTypes ) { + return $baseType; + } + $codecs = strtolower( implode( ", ", $streamTypes ) ); + return $baseType . '; codecs="' . $codecs . '"'; + } + /** + * @param $file File + * @return array|bool + */ + function getStreamTypes( $file ) { + $streamTypes = array(); + $metadata = $this->unpackMetadata( $file->getMetadata() ); + if ( !$metadata || isset( $metadata['error'] ) ) { + return false; + } + foreach ( $metadata['streams'] as $stream ) { + $streamTypes[] = $stream['type']; + } + return array_unique( $streamTypes ); + } + + /** + * @param $file File + * @return int + */ + function getOffset( $file ){ + $metadata = $this->unpackMetadata( $file->getMetadata() ); + if ( !$metadata || isset( $metadata['error'] ) || !isset( $metadata['offset']) ) { + return 0; + } else { + return $metadata['offset']; + } + } + + /** + * @param $file File + * @return int + */ + function getLength( $file ) { + $metadata = $this->unpackMetadata( $file->getMetadata() ); + if ( !$metadata || isset( $metadata['error'] ) ) { + return 0; + } else { + return $metadata['length']; + } + } + + /** + * Get useful response headers for GET/HEAD requests for a file with the given metadata + * @param $metadata mixed Result this handlers getMetadata() for a file + * @return Array + */ + public function getStreamHeaders( $metadata ) { + $metadata = $this->unpackMetadata( $metadata ); + if ( $metadata && !isset( $metadata['error'] ) && isset( $metadata['length'] ) ) { + return array( 'X-Content-Duration' => floatval( $metadata[ 'length' ] ) ); + } + return array(); + } + + /** + * @param $file File + * @return float|int + */ + function getFramerate( $file ){ + $metadata = $this->unpackMetadata( $file->getMetadata() ); + if ( !$metadata || isset( $metadata['error'] ) ) { + return 0; + } else { + // Return the first found theora stream framerate: + foreach ( $metadata['streams'] as $stream ) { + if( $stream['type'] == 'Theora' ){ + return $stream['header']['FRN'] / $stream['header']['FRD']; + } + } + return 0; + } + } + + /** + * @param $file File + * @return String + */ + function getShortDesc( $file ) { + global $wgLang, $wgMediaAudioTypes, $wgMediaVideoTypes; + + $streamTypes = $this->getStreamTypes( $file ); + if ( !$streamTypes ) { + return parent::getShortDesc( $file ); + } + if ( array_intersect( $streamTypes, $wgMediaVideoTypes ) ) { + // Count multiplexed audio/video as video for short descriptions + $msg = 'timedmedia-ogg-short-video'; + } elseif ( array_intersect( $streamTypes, $wgMediaAudioTypes ) ) { + $msg = 'timedmedia-ogg-short-audio'; + } else { + $msg = 'timedmedia-ogg-short-general'; + } + return wfMessage( $msg, implode( '/', $streamTypes ), + $wgLang->formatTimePeriod( $this->getLength( $file ) ) )->text(); + } + + /** + * @param $file File + * @return String + */ + function getLongDesc( $file ) { + global $wgLang, $wgMediaVideoTypes, $wgMediaAudioTypes; + + $streamTypes = $this->getStreamTypes( $file ); + if ( !$streamTypes ) { + $unpacked = $this->unpackMetadata( $file->getMetadata() ); + if ( isset( $unpacked['error']['message'] ) ) { + return wfMessage( 'timedmedia-ogg-long-error', $unpacked['error']['message'] )->text(); + } else { + return wfMessage( 'timedmedia-ogg-long-no-streams' )->text(); + } + } + if ( array_intersect( $streamTypes,$wgMediaVideoTypes ) ) { + if ( array_intersect( $streamTypes, $wgMediaAudioTypes ) ) { + $msg = 'timedmedia-ogg-long-multiplexed'; + } else { + $msg = 'timedmedia-ogg-long-video'; + } + } elseif ( array_intersect( $streamTypes, $wgMediaAudioTypes ) ) { + $msg = 'timedmedia-ogg-long-audio'; + } else { + $msg = 'timedmedia-ogg-long-general'; + } + $size = 0; + $unpacked = $this->unpackMetadata( $file->getMetadata() ); + if ( !$unpacked || isset( $metadata['error'] ) ) { + $length = 0; + } else { + $length = $this->getLength( $file ); + foreach ( $unpacked['streams'] as $stream ) { + if( isset( $stream['size'] ) ) + $size += $stream['size']; + } + } + return wfMessage( + $msg, + implode( '/', $streamTypes ), + $wgLang->formatTimePeriod( $length ), + $wgLang->formatBitrate( $this->getBitRate( $file ) ) + )->numParams( + $file->getWidth(), + $file->getHeight() + )->text(); + } + + /** + * @param $file File + * @return float|int + */ + function getBitRate( &$file ){ + $size = 0; + $unpacked = $this->unpackMetadata( $file->getMetadata() ); + if ( !$unpacked || isset( $unpacked['error'] ) ) { + $length = 0; + } else { + $length = $this->getLength( $file ); + if ( isset( $unpacked['streams'] ) ) { + foreach ( $unpacked['streams'] as $stream ) { + if( isset( $stream['size'] ) ) + $size += $stream['size']; + } + } + } + return $length == 0 ? 0 : $size / $length * 8; + } +} |