r56771 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r56770‎ | r56771 | r56772 >
Date:16:53, 22 September 2009
Author:dale
Status:deferred
Tags:
Comment:
* stored the in-development oggServer near the flvServer
Modified paths:
  • /trunk/extensions/MetavidWiki/skins/mv_embed/oggServer/OggChop.php (added) (history)
  • /trunk/extensions/MetavidWiki/skins/mv_embed/oggServer/OggChopRequestHanlder.php (added) (history)

Diff [purge]

Index: trunk/extensions/MetavidWiki/skins/mv_embed/oggServer/OggChopRequestHanlder.php
@@ -0,0 +1,45 @@
 2+<?php
 3+/*
 4+ * OggChopRequestHanlder.php is a simple ogg video server for mediawiki
 5+ * it uses the general oggChop class (which we should probably make part of
 6+ * PEAR oggServer or something like that )
 7+ *
 8+ * it takes arguments http arguments:
 9+ * file=WikiFileTitle.ogg
 10+ * t=start_time_npt/end_time_npt
 11+ *
 12+ * if no filename.ogg.meta file is available it generates it.
 13+ * (this can be slow its recommended you pre-generate the .meta files)
 14+ *
 15+ * * This is just a fallback solution / prototypeing for oggz_chop fastCGI.\
 16+ * this will just server from the nearest keyframe
 17+*/
 18+
 19+//for now just hard code the request:
 20+//$oggPath = '/var/www/house_proceeding_01-04-07.ogg';
 21+$oggPath = '/var/www/lucky.ogv';
 22+$time = '0:0:10/0:0:20';
 23+
 24+require_once( 'OggChop.php' );
 25+
 26+$ogg = new OggChop( $oggPath );
 27+$ogg->play();
 28+die();
 29+
 30+/*utility functions*/
 31+function npt2seconds( $str_time ) {
 32+ $time_ary = explode( ':', $str_time );
 33+ $hours = $min = $sec = 0;
 34+ if ( count( $time_ary ) == 3 ) {
 35+ $hours = (int) $time_ary[0];
 36+ $min = (int) $time_ary[1];
 37+ $sec = (float) $time_ary[2];
 38+ } else if ( count( $time_ary ) == 2 ) {
 39+ $min = (int) $time_ary[0];
 40+ $sec = (float) $time_ary[1];
 41+ } else if ( count( $time_ary ) == 1 ) {
 42+ $sec = (float) $time_ary[0];
 43+ }
 44+ return ( $hours * 3600 ) + ( $min * 60 ) + $sec;
 45+}
 46+?>
\ No newline at end of file
Index: trunk/extensions/MetavidWiki/skins/mv_embed/oggServer/OggChop.php
@@ -0,0 +1,260 @@
 2+<?php
 3+
 4+define('OGGCHOP_META_VERSION', 1);
 5+
 6+define('OGGCHOP_META_EXT', '.meta');
 7+
 8+$oggDir = dirname(__FILE__);
 9+
 10+//require the PEAR php module
 11+ini_set( 'include_path',
 12+ "$oggDir/PEAR/File_Ogg" .
 13+ PATH_SEPARATOR .
 14+ ini_get( 'include_path' ));
 15+
 16+class OggChop {
 17+ //initial variable values:
 18+ var $meta = false;
 19+ var $loadWaitCount = 0;
 20+
 21+ //header values:
 22+ var $contentLength = 0;
 23+
 24+ function __construct( $oggPath ){
 25+ $this->oggPath = $oggPath;
 26+ }
 27+ /*
 28+ * play takes in start_sec and end_sec and sends out packet
 29+ *
 30+ * @param float $start_sec ( start time in float seconds)
 31+ * @param floast $end_sec (optional end time in float)
 32+ */
 33+ function play($start_sec=false, $end_sec = false){
 34+ //make sure we have the metadata ready:
 35+ $this->loadMeta();
 36+
 37+ //get http byte range headers::
 38+ $this->getByteRangeRequest();
 39+
 40+ //if both start and end are false send the full file:
 41+ if(!$start_sec && !$end_sec){
 42+ //set from full file context::
 43+ $this->contentLength = filesize( $this->oggPath );
 44+ $this->contentRange = array(
 45+ 's' => 0,
 46+ 'e' => $this->contentLength -1,
 47+ 't' => $this->contentLength
 48+ );
 49+ $this->duration = $this->getMeta('duration');
 50+ $this->sendHeaders();
 51+ //output the full file:
 52+
 53+ //turn off output buffering
 54+ while (ob_get_level() > 0) {
 55+ ob_end_flush();
 56+ }
 57+ @readfile( $this->oggPath );
 58+ //exit the application (might be a cleaner way to do this)
 59+ die();
 60+ }else{
 61+ $kEnd = false;
 62+ //we have a temporal request
 63+ if(!$start_sec || $start_sec < 0)
 64+ $start_sec = 0;
 65+
 66+ if(!$end_sec || $end_sec > $this->getMeta('duration')){
 67+ $end_sec = $this->getMeta('duration');
 68+ $kEnd = array(
 69+ $this->getMeta('duration'),
 70+ filesize( $this->oggPath )
 71+ );
 72+ }
 73+
 74+ //set the duration:
 75+ $this->duration = $end_sec - $start_sec;
 76+
 77+ //set the content size for the segment:
 78+ $kStart = $this->getKeyFrameByteFromTime( $start_sec );
 79+ if( ! $kEnd )
 80+ $kEnd = $this->getKeyFrameByteFromTime( $end_sec , false);
 81+
 82+ //debug output:
 83+ /*
 84+ print_r($this->meta['theoraKeyFrameInx']);
 85+ print "Start : ". print_r($kStart, true) ."\n";
 86+ print "End Byte:" . print_r($kEnd, true) . "\n";
 87+ die();
 88+
 89+ @@todo build the ogg skeleton header
 90+ 1) that gives the offset between
 91+ // $kStart time and the requested time.
 92+
 93+ //for now just start output at the given byte range
 94+ $this->outputByteRange( $kStart[1], $kEnd[1]);
 95+ }
 96+
 97+ }
 98+ function getKeyFrameByteFromTime( $reqTime, $prev_key=true ){
 99+ //::binary array search goes here::
 100+
 101+ //linear search (may be faster in some cases (like start of the file seeks)
 102+ $timeDiff = $this->getMeta('duration');
 103+ reset($this->meta['theoraKeyFrameInx']);
 104+ $pByte = current( $this->meta['theoraKeyFrameInx'] );
 105+ $pKtime = key( $this->meta['theoraKeyFrameInx'] );
 106+ foreach($this->meta['theoraKeyFrameInx'] as $kTime => $byte){
 107+ if($kTime > $reqTime)
 108+ break;
 109+ $pByte = $byte;
 110+ $pKtime = $kTime;
 111+ }
 112+ //return the keyframe array by default the prev key
 113+ if($prev_key){
 114+ return array($pKtime, $pByte);
 115+ }else{
 116+ return array($kTime, $byte);
 117+ }
 118+ }
 119+ /*
 120+ * outputByteRange
 121+ */
 122+ function outputByteRange($startByte, $endByte = null){
 123+ //media files use large chunk size:
 124+ $chunkSize = 32768;
 125+ $this->fp = fopen( $this->oggPath, 'r');
 126+ fseek($this->fp, $startByte);
 127+ while (! feof($this->fp))
 128+ {
 129+ if( $endByte != null ){
 130+ if( ftell( $this->fp ) + $chunkSize > $endByte ){
 131+ $read_amount = ( ftell ( $this->fp ) + $chunkSize ) - $endByte;
 132+ echo fread($this->fp, $read_amount);
 133+ break;
 134+ }
 135+ }
 136+ echo fread($this->fp, $chunkSize);
 137+ }
 138+ }
 139+ function getByteRangeRequest(){
 140+ //set local vars for byte range request handling
 141+ }
 142+ function sendHeaders(){
 143+ header ("Accept-Ranges: bytes");
 144+
 145+ //set range conditional headers:
 146+ if( $this->contentLength )
 147+ header ( "Content-Length: " . $this->contentLength );
 148+
 149+ //set the X-content duration:
 150+ if( $this->duration )
 151+ header ( "X-Content-Duration: " . $this->duration );
 152+
 153+ //set content range see spec:
 154+ // http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.16
 155+ if( $this->contentRange )
 156+ header ( "Content-Range: bytes " .
 157+ $this->contentRange['s'] . "-" .
 158+ $this->contentRange['e'] . "/" .
 159+ $this->contentRange['t']
 160+ );
 161+
 162+ //constant headers (for video)
 163+ if( isset($this->meta['height']) )
 164+ header( "X-Content-Video-Height: " . $this->meta['height'] );
 165+
 166+ if( isset($this->meta['width']) )
 167+ header( "X-Content-Video-Width: " . $this->meta['width'] );
 168+
 169+ //set mime type (only video for now)
 170+ header ("Content-Type: video/ogg");
 171+
 172+ }
 173+ function sendByteRange( $startByte, $endByte){
 174+
 175+ }
 176+ /*
 177+ * getMeta (returns the value of a metadata key)
 178+ */
 179+ function getMeta( $key ){
 180+ if( !$this->meta ){
 181+ $this->loadMeta();
 182+ }
 183+ if( isset( $this->meta[$key] ) )
 184+ return $this->meta[ $key ];
 185+ return false;
 186+ }
 187+ function loadMeta(){
 188+ //load from the file:
 189+ if( is_file( $this->oggPath . OGGCHOP_META_EXT ) ){
 190+ $oggMeta = file_get_contents( $this->oggPath . OGGCHOP_META_EXT);
 191+ //check if a separate request is working on generating the file:
 192+ if( trim( $oggMeta ) == 'loading' ){
 193+ if( $this->loadWaitCount >= 24 ){
 194+ //we have waited 2 min with no luck..
 195+ //@@todo we should flag that ogg file as broken?
 196+ // and just redirect to normal output? (for now just set meta to false)
 197+ $this->meta = false;
 198+ //fail:
 199+ return false;
 200+ }else{
 201+ //some other request is "loading" metadata sleep for 5 seconds and try again
 202+ sleep(5);
 203+ $this->loadWaitCount++;
 204+ return $this->loadMeta();
 205+ }
 206+ }else{
 207+ $this->meta = unserialize ( $oggMeta );
 208+ if( $this->meta['version'] == 'OGGCHOP_META_VERSION' ){
 209+ //we have a good version of the metadata return true:
 210+ return true;
 211+ }else{
 212+ $this->meta = false;
 213+ }
 214+ }
 215+ }
 216+ //if the file does not exist or $this->meta is still false::
 217+ if( ! is_file( $this->oggPath . OGGCHOP_META_EXT ) || $this->meta === false ){
 218+ //set the meta file to "loading" (avoids multiple indexing requests)
 219+ file_put_contents( $this->oggPath . OGGCHOP_META_EXT, 'loading');
 220+
 221+ //load up the File/Ogg Pear module
 222+ if ( !class_exists( 'File_Ogg' ) ) {
 223+ require( 'File/Ogg.php' );
 224+ }
 225+ $f = new File_Ogg( $this->oggPath );
 226+ $streams = array();
 227+ $this->meta = array(
 228+ 'version' => OGGCHOP_META_VERSION
 229+ );
 230+ foreach ( $f->listStreams() as $streamType => $streamIDs ) {
 231+ foreach ( $streamIDs as $streamID ) {
 232+ $stream = $f->getStream( $streamID );
 233+ //for now only support a fist theora stream we find:
 234+ if( strtolower( $stream->getType() ) == 'theora'){
 235+ $this->meta['theoraKeyFrameInx'] = $stream->getKeyFrameIndex();
 236+ //set the width and height:
 237+ $head = $stream->getHeader();
 238+ $this->meta['width'] = $head['PICW'];
 239+ $this->meta['height'] = $head['PICH'];
 240+ break;
 241+ }
 242+ /* more detailed per-stream metadata::
 243+ * $this->meta['streams'][$streamID] = array(
 244+ 'serial' => $stream->getSerial(),
 245+ 'group' => $stream->getGroup(),
 246+ 'type' => $stream->getType(),
 247+ 'vendor' => $stream->getVendor(),
 248+ 'length' => $stream->getLength(),
 249+ 'size' => $stream->getSize(),
 250+ 'header' => $stream->getHeader(),
 251+ 'comments' => $stream->getComments()
 252+ );*/
 253+ }
 254+ }
 255+ $this->meta['duration'] = $f->getLength();
 256+ //cahce the metadata::
 257+ file_put_contents( $this->oggPath . OGGCHOP_META_EXT, serialize( $this->meta) );
 258+ return true;
 259+ }
 260+ }
 261+}

Status & tagging log