Index: trunk/extensions/OggHandler/OggTranscode.sql |
— | — | @@ -0,0 +1,29 @@ |
| 2 | +-- |
| 3 | +-- Table structure for table `transcode_job` |
| 4 | +-- |
| 5 | + |
| 6 | +CREATE TABLE IF NOT EXISTS /*_*/transcode_job ( |
| 7 | + |
| 8 | + --File name key ( called image to match table name ) |
| 9 | + tjob_name varbinary(255) NOT NULL, |
| 10 | + |
| 11 | + --Derivative key of the transcode |
| 12 | + tjob_derivative_key varbinary(40) NOT NULL, |
| 13 | + |
| 14 | + -- Timestamp |
| 15 | + `tjob_start_timestamp` char(14) NOT NULL, |
| 16 | + |
| 17 | + -- Transcoding state |
| 18 | + tjob_state enum('OK','ASSIGNED','FAILED') default NULL |
| 19 | + |
| 20 | +) /*$wgDBTableOptions*/; |
| 21 | + |
| 22 | +-- |
| 23 | +-- Index for transcode_job_name |
| 24 | +-- |
| 25 | +CREATE INDEX /*i*/transcode_job_name ON /*_*/transcode_job ( tjob_name ); |
| 26 | + |
| 27 | +-- |
| 28 | +-- Index for tjob_state |
| 29 | +-- |
| 30 | +CREATE INDEX /*i*/transcode_job_state ON /*_*/transcode_job ( tjob_state ); |
Index: trunk/extensions/OggHandler/OggHandler_body.php |
— | — | @@ -195,6 +195,15 @@ |
196 | 196 | // Set up the default targetUrl: |
197 | 197 | $targetFileUrl = $file->getURL(); |
198 | 198 | |
| 199 | + // Check for $wgEnabledDerivatives |
| 200 | + if ( isset( $wgEnabledDerivatives ) && |
| 201 | + is_array( $wgEnabledDerivatives ) && |
| 202 | + count( $wgEnabledDerivatives ) != 0 |
| 203 | + ){ |
| 204 | + // Get the derivative via embed width |
| 205 | + //( will return $file->getURL() if no derivative is found ) |
| 206 | + $targetFileUrl = OggTranscode::getWidthDerivativeURL( $file, $width); |
| 207 | + } |
199 | 208 | |
200 | 209 | // Add temporal request parameter if $wgEnableTemporalOggUrls is on: |
201 | 210 | if( $wgEnableTemporalOggUrls && isset( $params['start'] ) ){ |
— | — | @@ -203,31 +212,7 @@ |
204 | 213 | $targetFileUrl.='/'. seconds2npt( $this->parseTimeString( $params['end'], $length) ); |
205 | 214 | } |
206 | 215 | |
207 | | - // Check if $wgEnabledDerivatives is "set" and we have a target derivative set: |
208 | | - // (presently set by the wikiAtHome extension) |
209 | | - if (isset( $wgEnabledDerivatives ) && is_array( $wgEnabledDerivatives ) && count( $wgEnabledDerivatives ) != 0){ |
210 | | - //get the encode key: |
211 | | - $encodeKey = WikiAtHome::getTargetDerivative( $width, $file ); |
212 | | - if( $encodeKey == 'notransform'){ |
213 | | - $targetFileUrl = $file->getURL() ; |
214 | | - }else{ |
215 | | - //get our job pointer |
216 | | - $wjm = WahJobManager::newFromFile( $file , $encodeKey ); |
217 | 216 | |
218 | | - $derivativeUrl = $file->getThumbUrl( $wjm->getEncodeKey() . '.ogg'); |
219 | | - $derivativePath = $file->getThumbPath( $wjm->getEncodeKey() ); |
220 | | - |
221 | | - // Check that we have the requested theora derivative |
222 | | - if( is_file ( "{$derivativePath}.ogg" )){ |
223 | | - $targetFileUrl = $derivativeUrl; |
224 | | - }else{ |
225 | | - // Output in queue text (send the dstUrl if available ) |
226 | | - return new MediaQueueTransformOutput($file, $dstUrl, $width, $height, $wjm->getDonePerc() ); |
227 | | - } |
228 | | - } |
229 | | - } |
230 | | - |
231 | | - |
232 | 217 | if ( !$noPlayer ) { |
233 | 218 | // Hack for miscellaneous callers |
234 | 219 | global $wgOut; |
Index: trunk/extensions/OggHandler/patch-image-index.sql |
— | — | @@ -0,0 +1,14 @@ |
| 2 | +-- |
| 3 | +-- Add an index on img_media_type |
| 4 | +-- |
| 5 | +CREATE INDEX /*i*/image_media_type ON /*_*/image ( img_media_type ); |
| 6 | + |
| 7 | +-- |
| 8 | +-- Index for transcode_job_name |
| 9 | +-- |
| 10 | +CREATE INDEX /*i*/transcode_job_name ON /*_*/transcode_job ( tjob_name ); |
| 11 | + |
| 12 | +-- |
| 13 | +-- Index for tjob_state |
| 14 | +-- |
| 15 | +CREATE INDEX /*i*/transcode_job_state ON /*_*/transcode_job ( tjob_state ); |
\ No newline at end of file |
Index: trunk/extensions/OggHandler/OggTranscodeCron.php |
— | — | @@ -0,0 +1,318 @@ |
| 2 | +<?php |
| 3 | + |
| 4 | +/* |
| 5 | +* OggTranscodeCron.php |
| 6 | +* |
| 7 | +* Maintenance script overview: |
| 8 | +* |
| 9 | +* Designed to be run every 10 seconds or so. Will spawn threads |
| 10 | +* until max thread count reached |
| 11 | +* |
| 12 | +* 1) Check if number of instances is < $wgMaxTranscodeThreads |
| 13 | +* the script exits immediately. |
| 14 | +* |
| 15 | +* 2) Query the db for jobSets ( find a job that is not processed or > wgTranscodeJobTimeOut ) |
| 16 | +* |
| 17 | +* 3) Does a incomplete transcode to temporary location |
| 18 | +* once done moves file into place. |
| 19 | +* |
| 20 | +*/ |
| 21 | +require_once( dirname(__FILE__) . '/../../js2-work/maintenance/Maintenance.php' ); |
| 22 | + |
| 23 | +class OggTranscodeCron extends Maintenance { |
| 24 | + |
| 25 | + // The max number of threads ( can also be set when called via the command line ) |
| 26 | + private $maxThreads = 2; |
| 27 | + |
| 28 | + public function __construct() { |
| 29 | + global $wgUseNormalUser; |
| 30 | + parent::__construct(); |
| 31 | + $this->mDescription = "Transcode video files"; |
| 32 | + $this->addOption( 'maxThreads', 'Maximum number of threads to run', false, true ); |
| 33 | + $wgUseNormalUser = true; |
| 34 | + } |
| 35 | + |
| 36 | + /** |
| 37 | + * Main Execute method from Maintenance class |
| 38 | + */ |
| 39 | + public function execute() { |
| 40 | + global $wgEnabledDerivatives; |
| 41 | + // Set command line options: |
| 42 | + if ( $this->hasOption( 'maxThreads' ) ) { |
| 43 | + $this->maxThreads = intval( $this->hasOption( 'maxThreads' ) ); |
| 44 | + } |
| 45 | + |
| 46 | + // Check if number of instances is < $wgMaxTranscodeThreads |
| 47 | + if( $this->checkMaxThreads() ){ |
| 48 | + exit( 0 ); |
| 49 | + } |
| 50 | + |
| 51 | + // Check if we have $wgEnabledDerivatives to transcode to. |
| 52 | + if( !isset( $wgEnabledDerivatives ) || count( $wgEnabledDerivatives ) == 0 ){ |
| 53 | + print "\$wgEnabledDerivatives is not set or empty \n"; |
| 54 | + exit( 0 ); |
| 55 | + } |
| 56 | + |
| 57 | + // Check if there are any jobs to be done |
| 58 | + $job = $this->getTranscodeJob(); |
| 59 | + if( $job ){ |
| 60 | + if( ! $this->processTranscodeJob( $job ) ){ |
| 61 | + // Mark the job as failed if it did not complete |
| 62 | + $this->markJobState( $job, 'FAILED' ); |
| 63 | + exit( 1 ); |
| 64 | + } |
| 65 | + // Else job was oky mark state and exit: |
| 66 | + $this->markJobState( $job, 'OK' ); |
| 67 | + exit( 0 ); |
| 68 | + } |
| 69 | + // No jobs found. |
| 70 | + |
| 71 | + // Update Transcode Jobs table with images that are missing transcode jobs |
| 72 | + // ( the updated transcode job set will be accessed the next time the script is run) |
| 73 | + $this->updateTranscodeJobs(); |
| 74 | + exit( 0 ); |
| 75 | + } |
| 76 | + |
| 77 | + /** |
| 78 | + * Get a Transcode Job from the transcode_job table |
| 79 | + */ |
| 80 | + function getTranscodeJob() { |
| 81 | + $dbr = & wfGetDB( DB_SLAVE ); |
| 82 | + |
| 83 | + // Don't hit the slaves under high load: |
| 84 | + wfWaitForSlaves( 5 ); |
| 85 | + |
| 86 | + // Grab transcode job |
| 87 | + $res = $dbr->select( |
| 88 | + 'transcode_job', |
| 89 | + '*', |
| 90 | + 'tjob_state IS NULL', |
| 91 | + __METHOD__, |
| 92 | + array( |
| 93 | + 'USE INDEX' => array( |
| 94 | + 'transcode_job' => 'transcode_job_state' |
| 95 | + ), |
| 96 | + 'LIMIT' => 20 |
| 97 | + ) |
| 98 | + ); |
| 99 | + return $dbr->fetchObject( $res ); |
| 100 | + } |
| 101 | + |
| 102 | + /** |
| 103 | + * Grab a batch of up-to 100 jobs and insert them into the jobs table. |
| 104 | + * |
| 105 | + * NOTE: this presently does not test for missing derivative keys / |
| 106 | + * keep derivative keys "in-sync" with transcode_job table. |
| 107 | + */ |
| 108 | + function updateTranscodeJobs( ){ |
| 109 | + global $wgEnabledDerivatives; |
| 110 | + // Find new jobs and insert them into the db |
| 111 | + $dbr = & wfGetDB( DB_SLAVE ); |
| 112 | + |
| 113 | + // Don't hit the slaves under high load: |
| 114 | + wfWaitForSlaves( 5 ); |
| 115 | + |
| 116 | + // Get image files that don't have a transcode job |
| 117 | + $res = $dbr->select( |
| 118 | + array( 'image', 'transcode_job'), |
| 119 | + array( 'img_name' ), |
| 120 | + array( 'tjob_name IS NULL', |
| 121 | + 'img_media_type' => 'VIDEO'), |
| 122 | + __METHOD__, |
| 123 | + array( 'LIMIT' => 100 ), |
| 124 | + array( |
| 125 | + 'transcode_job' => array( |
| 126 | + 'LEFT JOIN', |
| 127 | + array( 'img_name = tjob_name' ) |
| 128 | + ) |
| 129 | + ) |
| 130 | + ); |
| 131 | + $imageNames = array(); |
| 132 | + while( $row = $dbr->fetchObject( $res ) ) { |
| 133 | + $imageNames[] = $row->img_name; |
| 134 | + } |
| 135 | + if( count( $imageNames ) == 0){ |
| 136 | + return false; |
| 137 | + } |
| 138 | + |
| 139 | + // Add the derivative set[s] to the transcode table |
| 140 | + $dbw = & wfGetDB( DB_MASTER ); |
| 141 | + $dbw->begin(); |
| 142 | + foreach( $imageNames as $name){ |
| 143 | + foreach( $wgEnabledDerivatives as $derivativeKey ){ |
| 144 | + |
| 145 | + // Do not to add a derivative job that is > than the source asset |
| 146 | + $fileTitle = Title::newFromText( $name, NS_FILE); |
| 147 | + |
| 148 | + $file = wfFindFile( $fileTitle ); |
| 149 | + if( !$file ){ |
| 150 | + $this->output( "File not found: {$name} (not adding to jobQueue\n" ); |
| 151 | + continue; |
| 152 | + } |
| 153 | + $targetSize = OggTranscode::$derivativeSettings[ $derivativeKey ]['maxSize']; |
| 154 | + $sourceSize = $file->getWidth(); |
| 155 | + if( $targetSize > $sourceSize ){ |
| 156 | + $this->output( "File:{$name} is too small for {$derivativeKey} ::\n" . |
| 157 | + "target: {$targetSize} > source: {$sourceSize} \n\n" ); |
| 158 | + continue; |
| 159 | + } |
| 160 | + // Else $derivative size is < source size, do insert: |
| 161 | + $dbw->insert( |
| 162 | + 'transcode_job', |
| 163 | + array( |
| 164 | + 'tjob_name' => $name, |
| 165 | + 'tjob_derivative_key' => $derivativeKey, |
| 166 | + 'tjob_start_timestamp' => time() |
| 167 | + ), |
| 168 | + __METHOD__ |
| 169 | + ); |
| 170 | + } |
| 171 | + } |
| 172 | + $dbw->commit(); |
| 173 | + } |
| 174 | + |
| 175 | + /** |
| 176 | + * Process a transcode job |
| 177 | + * @param {Object} $job Transcode job db row |
| 178 | + * @return {Bollean} |
| 179 | + * true if job was processed |
| 180 | + * false if there was an error in the job |
| 181 | + */ |
| 182 | + function processTranscodeJob( $job ){ |
| 183 | + $dbw = & wfGetDB( DB_MASTER ); |
| 184 | + |
| 185 | + // Update job state to "ASSIGNED" |
| 186 | + $this->markJobState( $job, 'ASSIGNED'); |
| 187 | + |
| 188 | + // Get the source title from tjob_name |
| 189 | + $fileTitle = Title::newFromText( $job->tjob_name, NS_FILE); |
| 190 | + if( ! $fileTitle->exists() ){ |
| 191 | + $this->output( "Title not found: {$job->tjob_name}\n" ); |
| 192 | + return false; |
| 193 | + } |
| 194 | + |
| 195 | + // Get the file source: |
| 196 | + $file = wfFindFile( $fileTitle ); |
| 197 | + if( !$file ){ |
| 198 | + $this->output( "File not found: {$job->tjob_name}\n" ); |
| 199 | + return false; |
| 200 | + } |
| 201 | + |
| 202 | + // Setup a temporary target location for encode output |
| 203 | + $tempTarget = wfTempDir() .'/'. $fileTitle->getDbKey() . $job->tjob_derivative_key . '.ogv'; |
| 204 | + |
| 205 | + // Get encoding settings from encode key |
| 206 | + if( !isset( OggTranscode::$derivativeSettings[ $job->tjob_derivative_key ] )){ |
| 207 | + $this->output( "Derivative key not found: {$job->tjob_name}\n" ); |
| 208 | + return false; |
| 209 | + } |
| 210 | + $encodeSettings = OggTranscode::$derivativeSettings[ $job->tjob_derivative_key ]; |
| 211 | + |
| 212 | + // Shell out the transcode command. |
| 213 | + $this->output( "Starting transcode:\n" ); |
| 214 | + $result = $this->doEncode( $file->getPath(), $tempTarget, $encodeSettings ); |
| 215 | + |
| 216 | + // Check the result |
| 217 | + if( !$result ){ |
| 218 | + $this->output( "Transcode failed: {$job->tjob_name}\n" ); |
| 219 | + return false; |
| 220 | + } |
| 221 | + |
| 222 | + // If result is "ok" |
| 223 | + $this->output( "Transcode OK, moving thumb to public location\n" ); |
| 224 | + $derivativeTarget = $file->getThumbPath( $job->tjob_derivative_key ) . '.ogv'; |
| 225 | + |
| 226 | + if( !rename( $tempTarget, $derivativeTarget) ){ |
| 227 | + $this->output( "Renamed failed: {$tempTarget} to {$derivativeTarget}\n" ); |
| 228 | + return false; |
| 229 | + } |
| 230 | + // Return true to update the job state: |
| 231 | + return true; |
| 232 | + } |
| 233 | + /** |
| 234 | + * Mark a job as state |
| 235 | + * @param {Object} $job Job db row object. |
| 236 | + * @param {String} $state String to update state to can be: |
| 237 | + * 'OK','ASSIGNED','FAILED' |
| 238 | + */ |
| 239 | + function markJobState( $job , $state){ |
| 240 | + $dbw = & wfGetDB( DB_MASTER ); |
| 241 | + $dbw->update( |
| 242 | + 'transcode_job', |
| 243 | + array( |
| 244 | + 'tjob_state' => $state |
| 245 | + ), |
| 246 | + array( |
| 247 | + 'tjob_name' => $job->tjob_name, |
| 248 | + 'tjob_derivative_key' => $job->tjob_derivative_key |
| 249 | + ) |
| 250 | + ); |
| 251 | + } |
| 252 | + |
| 253 | + /** |
| 254 | + * Check the number if the instances of this script that are running |
| 255 | + * are over the max allowed threads $this->maxThreads |
| 256 | + * |
| 257 | + * @return {Boolean} |
| 258 | + * true if max Threads count has been reached |
| 259 | + * false if less than max Threads |
| 260 | + */ |
| 261 | + public function checkMaxThreads(){ |
| 262 | + |
| 263 | + // Get the list of processes: |
| 264 | + $pslist = wfShellExec( "ps aux", $out); |
| 265 | + |
| 266 | + // Return the count of php OggTranscodeCron.php found |
| 267 | + $threadCount = preg_match_all( '/php\sOggTranscodeCron\.php/', $pslist, $matches); |
| 268 | + if( $threadCount === false ){ |
| 269 | + print "Error in preg_match"; |
| 270 | + exit( 1 ); |
| 271 | + } |
| 272 | + return ( $threadCount > $this->maxThreads ); |
| 273 | + } |
| 274 | + |
| 275 | + /** |
| 276 | + * Issues an encode command to ffmpeg2theora |
| 277 | + * |
| 278 | + * @param {String} $source Source file asset |
| 279 | + * @param {String} $target Target output encode |
| 280 | + * @param {Array} $encodeSettings Settings to encode the file with |
| 281 | + */ |
| 282 | + function doEncode( $source, $target, $encodeSettings ){ |
| 283 | + global $wgffmpeg2theoraPath; |
| 284 | + |
| 285 | + // Set up the base command |
| 286 | + $cmd = wfEscapeShellArg( $wgffmpeg2theoraPath ) . ' ' . wfEscapeShellArg( $source ); |
| 287 | + |
| 288 | + // Add in the encode settings |
| 289 | + foreach( $encodeSettings as $key => $val){ |
| 290 | + if( isset( OggTranscode::$foggMap[$key] ) ){ |
| 291 | + if( is_array( OggTranscode::$foggMap[$key] ) ){ |
| 292 | + $cmd.= ' '. implode(' ', OggTranscode::$foggMap[$key] ); |
| 293 | + }else if($val == 'true' || $val === true){ |
| 294 | + $cmd.= ' '. OggTranscode::$foggMap[$key]; |
| 295 | + }else if( $val === false){ |
| 296 | + //ignore "false" flags |
| 297 | + }else{ |
| 298 | + //normal get/set value |
| 299 | + $cmd.= ' '. OggTranscode::$foggMap[$key] . ' ' . wfEscapeShellArg( $val ); |
| 300 | + } |
| 301 | + } |
| 302 | + } |
| 303 | + // Add the output target: |
| 304 | + $cmd.= ' -o ' . wfEscapeShellArg ( $target ); |
| 305 | + $this->output( "Running cmd: \n\n" .$cmd . "\n\n" ); |
| 306 | + wfProfileIn( 'ffmpeg2theora_encode' ); |
| 307 | + wfShellExec( $cmd, $retval ); |
| 308 | + wfProfileOut( 'ffmpeg2theora_encode' ); |
| 309 | + |
| 310 | + if( $retval ){ |
| 311 | + return false; |
| 312 | + } |
| 313 | + return true; |
| 314 | + } |
| 315 | +} |
| 316 | + |
| 317 | +$maintClass = "OggTranscodeCron"; |
| 318 | +require_once( DO_MAINTENANCE ); |
| 319 | +?> |
\ No newline at end of file |
Index: trunk/extensions/OggHandler/OggHandler.php |
— | — | @@ -7,6 +7,7 @@ |
8 | 8 | |
9 | 9 | $oggDir = dirname(__FILE__); |
10 | 10 | $wgAutoloadClasses['OggHandler'] = "$oggDir/OggHandler_body.php"; |
| 11 | +$wgAutoloadClasses['OggTranscode'] = "$oggDir/OggTranscode.php"; |
11 | 12 | |
12 | 13 | $wgMediaHandlers['application/ogg'] = 'OggHandler'; |
13 | 14 | if ( !in_array( 'ogg', $wgFileExtensions ) ) { |
— | — | @@ -32,9 +33,12 @@ |
33 | 34 | $wgHooks['LanguageGetMagic'][] = 'OggHandler::registerMagicWords'; |
34 | 35 | |
35 | 36 | |
36 | | -//Setup a hook for iframe=true (will strip the interface and only output the player) |
| 37 | +// Setup a hook for iframe=true (will strip the interface and only output the player) |
37 | 38 | $wgHooks['ArticleFromTitle'][] = 'OggHandler::iframeOutputHook'; |
38 | 39 | |
| 40 | +// Add the oggTranscode schema |
| 41 | +$wgHooks['LoadExtensionSchemaUpdates'][] = 'OggTranscode::schema'; |
| 42 | + |
39 | 43 | $wgExtensionCredits['media'][] = array( |
40 | 44 | 'path' => __FILE__, |
41 | 45 | 'name' => 'OggHandler', |
— | — | @@ -46,32 +50,32 @@ |
47 | 51 | |
48 | 52 | /******************* CONFIGURATION STARTS HERE **********************/ |
49 | 53 | |
50 | | -//set the supported ogg codecs: |
| 54 | +// Set the supported ogg codecs: |
51 | 55 | $wgOggVideoTypes = array( 'Theora' ); |
52 | 56 | $wgOggAudioTypes = array( 'Vorbis', 'Speex', 'FLAC' ); |
53 | 57 | |
54 | | -//if wgPlayerStats collection is enabled or not |
55 | | -$wgPlayerStatsCollection=false; |
| 58 | +// if wgPlayerStats collection is enabled or not |
| 59 | +$wgPlayerStatsCollection = false; |
56 | 60 | |
57 | | -// Output Video tag support |
| 61 | +// Output Video tag for player ( video tag is then rewritten to compatible player via mwEmbed ) |
58 | 62 | $wgVideoTagOut = false; |
59 | 63 | |
60 | 64 | // Defautl skin for mwEmbed player |
61 | 65 | // Skins presently available: |
62 | | -// "kskin" kaltura skin |
| 66 | +// "kskin" kaltura skin |
63 | 67 | // "mvpcf" a jquery ui like skin |
64 | 68 | $wgVideoPlayerSkin = 'kskin'; |
65 | 69 | |
66 | 70 | // Support striped player iframe output for remote embedding |
67 | 71 | $wgEnableIframeEmbed = false; |
68 | 72 | |
69 | | -// If timedText is enabled |
| 73 | +// Inline timedText reference url output |
70 | 74 | $wgEnableTimedText = false; |
71 | 75 | |
72 | | -//Location of oggThumb binary (used over the ffmpeg version) |
| 76 | +//Location of oggThumb binary ( used instead of ffmpeg ) |
73 | 77 | $wgOggThumbLocation = '/usr/bin/oggThumb'; |
74 | 78 | |
75 | | -//the location of ffmpeg2theora |
| 79 | +//the location of ffmpeg2theora ( for grabbing |
76 | 80 | $wgffmpeg2theoraPath = '/usr/bin/ffmpeg2theora'; |
77 | 81 | |
78 | 82 | // Location of the FFmpeg binary |
— | — | @@ -79,11 +83,29 @@ |
80 | 84 | |
81 | 85 | /** |
82 | 86 | * enable oggz_chop support |
83 | | - * if enabled the mv_embed player will use temporal urls |
| 87 | + * if enabled the mwEmbed player will use temporal urls |
84 | 88 | * for helping with seeking with some plugin types |
85 | 89 | */ |
86 | 90 | $wgEnableTemporalOggUrls = false; |
87 | 91 | |
| 92 | +// Enabled derivatives array |
| 93 | +// |
| 94 | +// Only derivatives with less width than the |
| 95 | +// source asset size will be created |
| 96 | +// |
| 97 | +// Derivatives can be created by running OggTranscodeCron.php |
| 98 | +// at regular intervals. The cron job |
| 99 | +// cycles through every ogg file and encodes the following derivative set: |
| 100 | +// |
| 101 | +// Derivative keys encode settings are defined in OggTranscode.php |
| 102 | +// |
| 103 | +$wgEnabledDerivatives = array( |
| 104 | + OggTranscode::ENC_WEB_2MBS, |
| 105 | + OggTranscode::ENC_WEB_4MBS, |
| 106 | + OggTranscode::ENC_WEB_6MBS, |
| 107 | + OggTranscode::ENC_HQ_VBR |
| 108 | +); |
| 109 | + |
88 | 110 | // Filename or URL path to the Cortado Java player applet. |
89 | 111 | // |
90 | 112 | // If no path is included, the path to this extension's |
— | — | @@ -100,35 +122,37 @@ |
101 | 123 | |
102 | 124 | /******************* CONFIGURATION ENDS HERE **********************/ |
103 | 125 | |
| 126 | +// NOTE: normally configuration based code would go into extension setup |
| 127 | +// so config could follow the oggHandler include but we need add the namespace early. |
104 | 128 | if( $wgEnableTimedText ){ |
105 | | -/** |
106 | | - * Handle Adding of "timedText" NameSpace |
107 | | - */ |
108 | | -$wgTimedTextNS = null; |
| 129 | + /** |
| 130 | + * Handle Adding of "timedText" NameSpace |
| 131 | + */ |
| 132 | + $wgTimedTextNS = null; |
109 | 133 | |
110 | | -// Make sure $wgExtraNamespaces in an array (set to NULL by default) : |
111 | | -if ( !is_array( $wgExtraNamespaces ) ) { |
112 | | - $wgExtraNamespaces = array(); |
113 | | -} |
114 | | -// Check for "TimedText" NS |
115 | | -$maxNS = 101; // content pages need "even" namespaces |
116 | | -foreach($wgExtraNamespaces as $ns => $nsTitle ){ |
117 | | - if( $nsTitle == 'TimedText' ){ |
118 | | - $wgTimedTextNS = $ns; |
| 134 | + // Make sure $wgExtraNamespaces in an array (set to NULL by default) : |
| 135 | + if ( !is_array( $wgExtraNamespaces ) ) { |
| 136 | + $wgExtraNamespaces = array(); |
119 | 137 | } |
120 | | - if( $ns > $maxNS ){ |
121 | | - $maxNs = $ns; |
| 138 | + // Check for "TimedText" NS |
| 139 | + $maxNS = 101; // content pages need "even" namespaces |
| 140 | + foreach($wgExtraNamespaces as $ns => $nsTitle ){ |
| 141 | + if( $nsTitle == 'TimedText' ){ |
| 142 | + $wgTimedTextNS = $ns; |
| 143 | + } |
| 144 | + if( $ns > $maxNS ){ |
| 145 | + $maxNs = $ns; |
| 146 | + } |
122 | 147 | } |
123 | | -} |
124 | | -// If not found add Add a custom timedText NS |
125 | | -if( !$wgTimedTextNS ){ |
126 | | - $wgTimedTextNS = ( $maxNS + 1 ); |
127 | | - $wgExtraNamespaces[ $wgTimedTextNS ] = 'TimedText'; |
128 | | - $wgExtraNamespaces[ $wgTimedTextNS +1 ] = 'TimedText_talk'; |
129 | | -} |
130 | | -define( "NS_TIMEDTEXT", $wgTimedTextNS); |
131 | | -// Assume $wgTimedTextNS +1 for talk |
132 | | -define( "NS_TIMEDTEXT_TALK", $wgTimedTextNS +1); |
| 148 | + // If not found add Add a custom timedText NS |
| 149 | + if( !$wgTimedTextNS ){ |
| 150 | + $wgTimedTextNS = ( $maxNS + 1 ); |
| 151 | + $wgExtraNamespaces[ $wgTimedTextNS ] = 'TimedText'; |
| 152 | + $wgExtraNamespaces[ $wgTimedTextNS +1 ] = 'TimedText_talk'; |
| 153 | + } |
| 154 | + define( "NS_TIMEDTEXT", $wgTimedTextNS); |
| 155 | + // Assume $wgTimedTextNS +1 for talk |
| 156 | + define( "NS_TIMEDTEXT_TALK", $wgTimedTextNS +1); |
133 | 157 | |
134 | 158 | |
135 | 159 | } // end of handling timedText |
\ No newline at end of file |
Index: trunk/extensions/OggHandler/OggTranscode.php |
— | — | @@ -0,0 +1,199 @@ |
| 2 | +<?php |
| 3 | + |
| 4 | +/* |
| 5 | + * OggTranscode provides keys and encode settings |
| 6 | + */ |
| 7 | + |
| 8 | + |
| 9 | +/* |
| 10 | + * Main OggTranscode Class hold some constants and config values |
| 11 | + */ |
| 12 | +class OggTranscode { |
| 13 | + /** |
| 14 | + * Key constants for the derivatives, |
| 15 | + * this key is appended to the derivative file name |
| 16 | + * |
| 17 | + * If you update the wgDerivativeSettings for one of these keys |
| 18 | + * and want to re-generate the video you should also update the |
| 19 | + * key constant. |
| 20 | + */ |
| 21 | + const ENC_WEB_2MBS = '256_200kbs'; |
| 22 | + const ENC_WEB_4MBS = '380_400kbs'; |
| 23 | + const ENC_WEB_6MBS = '480_600kbs'; |
| 24 | + const ENC_HQ_VBR = '720_VBR'; |
| 25 | + |
| 26 | + /** |
| 27 | + * Encoding parameters are set via firefogg encode api |
| 28 | + * |
| 29 | + * For clarity and compatibility with passing down |
| 30 | + * client side encode settings at point of upload |
| 31 | + * |
| 32 | + * http://firefogg.org/dev/index.html |
| 33 | + */ |
| 34 | + |
| 35 | + public static $derivativeSettings = array( |
| 36 | + OggTranscode::ENC_WEB_2MBS => |
| 37 | + array( |
| 38 | + 'maxSize' => '200', |
| 39 | + 'videoBitrate' => '128', |
| 40 | + 'audioBitrate' => '32', |
| 41 | + 'samplerate' => '22050', |
| 42 | + 'framerate' => '15', |
| 43 | + 'channels' => '1', |
| 44 | + 'noUpscaling' => 'true', |
| 45 | + 'twopass' => 'true', |
| 46 | + 'keyframeInterval' => '64', |
| 47 | + 'bufDelay' => '128' |
| 48 | + ), |
| 49 | + OggTranscode::ENC_WEB_4MBS => |
| 50 | + array( |
| 51 | + 'maxSize' => '320', |
| 52 | + 'videoBitrate' => '368', |
| 53 | + 'audioBitrate' => '48', |
| 54 | + 'noUpscaling' => 'true', |
| 55 | + 'twopass' => 'true', |
| 56 | + 'keyframeInterval' => '128', |
| 57 | + 'bufDelay' => '256' |
| 58 | + ), |
| 59 | + OggTranscode::ENC_WEB_6MBS => |
| 60 | + array( |
| 61 | + 'maxSize' => '480', |
| 62 | + 'videoBitrate' => '512', |
| 63 | + 'audioBitrate' => '96', |
| 64 | + 'noUpscaling' => 'true', |
| 65 | + 'twopass' => 'true', |
| 66 | + 'keyframeInterval' => '128', |
| 67 | + 'bufDelay' => '256' |
| 68 | + ), |
| 69 | + OggTranscode::ENC_HQ_VBR => |
| 70 | + array( |
| 71 | + 'maxSize' => '720', |
| 72 | + 'videoQuality' => 6, |
| 73 | + 'audioQuality' => 3, |
| 74 | + 'noUpscaling' => 'true' |
| 75 | + ) |
| 76 | + ); |
| 77 | + /** |
| 78 | + * Mapping between firefogg api and ffmpeg2theora command line |
| 79 | + * |
| 80 | + * This lets us share a common api between firefogg and oggTranscode |
| 81 | + * also see: http://firefogg.org/dev/index.html |
| 82 | + */ |
| 83 | + public static $foggMap = array( |
| 84 | + //video |
| 85 | + 'width' => "--width", |
| 86 | + 'height' => "--height", |
| 87 | + 'maxSize' => "--max_size", |
| 88 | + 'noUpscaling' => "--no-upscaling", |
| 89 | + 'videoQuality'=> "-v", |
| 90 | + 'videoBitrate' => "-V", |
| 91 | + 'twopass' => "--two-pass", |
| 92 | + 'framerate' => "-F", |
| 93 | + 'aspect' => "--aspect", |
| 94 | + 'starttime' => "--starttime", |
| 95 | + 'endtime' => "--endtime", |
| 96 | + 'cropTop' => "--croptop", |
| 97 | + 'cropBottom' => "--cropbottom", |
| 98 | + 'cropLeft' => "--cropleft", |
| 99 | + 'cropRight' => "--cropright", |
| 100 | + 'keyframeInterval'=> "--key", |
| 101 | + 'denoise' => array("--pp", "de"), |
| 102 | + 'novideo' => array("--novideo", "--no-skeleton"), |
| 103 | + 'bufDelay' => "--buf-delay", |
| 104 | + //audio |
| 105 | + 'audioQuality' => "-a", |
| 106 | + 'audioBitrate' => "-A", |
| 107 | + 'samplerate' => "-H", |
| 108 | + 'channels' => "-c", |
| 109 | + 'noaudio' => "--noaudio", |
| 110 | + //metadata |
| 111 | + 'artist' => "--artist", |
| 112 | + 'title' => "--title", |
| 113 | + 'date' => "--date", |
| 114 | + 'location' => "--location", |
| 115 | + 'organization' => "--organization", |
| 116 | + 'copyright' => "--copyright", |
| 117 | + 'license' => "--license", |
| 118 | + 'contact' => "--contact" |
| 119 | + ); |
| 120 | + /* |
| 121 | + * Setup the OggTranscode tables |
| 122 | + */ |
| 123 | + public static function schema() { |
| 124 | + global $wgExtNewTables, $wgExtNewIndexes; |
| 125 | + |
| 126 | + $wgExtNewTables[] = array( |
| 127 | + 'transcode_job', |
| 128 | + dirname( __FILE__ ) . '/OggTranscode.sql' |
| 129 | + ); |
| 130 | + |
| 131 | + $wgExtNewIndexes[] = array( |
| 132 | + 'image', 'image_media_type', |
| 133 | + dirname( __FILE__ ) . '/patch-image-index.sql' |
| 134 | + ); |
| 135 | + |
| 136 | + return true; |
| 137 | + } |
| 138 | + |
| 139 | + /** |
| 140 | + * Get derivative url for given embed width |
| 141 | + * |
| 142 | + * @param {Object} $srcFile Source file object |
| 143 | + * @param {Number} $targetWidth Target output width |
| 144 | + * @return {String} |
| 145 | + * the url for a given embed width, |
| 146 | + * or the srcFile url if no derivative found |
| 147 | + */ |
| 148 | + static function getWidthDerivativeURL( $srcFile, $targetWidth){ |
| 149 | + global $wgEnabledDerivatives; |
| 150 | + $srcWidth = $srcFile->getWidth(); |
| 151 | + $srcHeight = $srcFile->getHeight(); |
| 152 | + |
| 153 | + // Return source file url if targetdWidth greater than our source file. ) |
| 154 | + // should cover audio as well |
| 155 | + |
| 156 | + // NOTE removed: && get_class( $srcFile->handler) != 'NonFreeVideoHandler') |
| 157 | + if( $targetWidth >= $srcWidth ){ |
| 158 | + $srcFile->getURL(); |
| 159 | + } |
| 160 | + |
| 161 | + // Get the available derivatives by difference of 'maxSize' to 'targetWidth' |
| 162 | + $derivativeSet = array(); |
| 163 | + |
| 164 | + // include the source asset in the $derivativeSet distance comparison |
| 165 | + $derivativeSet[ 'SOURCE' ] = abs( $targetWidth - $srcWidth ); |
| 166 | + |
| 167 | + foreach( $wgEnabledDerivatives as $derivativeKey ){ |
| 168 | + if( isset( self::$derivativeSettings[ $derivativeKey ] ) ){ |
| 169 | + $derivativePath = $srcFile->getThumbPath( $derivativeKey ); |
| 170 | + // Check for derivative file: |
| 171 | + if( is_file ( "{$derivativePath}.ogv" )){ |
| 172 | + $maxSize = self::$derivativeSettings[ $derivativeKey ]['maxSize']; |
| 173 | + $derivativeSet[ $derivativeKey ] = abs( $targetWidth - $maxSize ); |
| 174 | + } |
| 175 | + } |
| 176 | + } |
| 177 | + |
| 178 | + // No derivative found return the src url |
| 179 | + if( count( $derivativeSet ) == 0 ){ |
| 180 | + return $srcFile->getURL(); |
| 181 | + } |
| 182 | + // Sort the $derivativeSet |
| 183 | + asort( $derivativeSet ); |
| 184 | + |
| 185 | + // Reset the pointer to start: |
| 186 | + reset( $derivativeSet ); |
| 187 | + |
| 188 | + // Handle special case where source is closes to target: |
| 189 | + if( key( $derivativeSet ) == 'SOURCE' ){ |
| 190 | + return $srcFile->getURL(); |
| 191 | + } |
| 192 | + |
| 193 | + // Get the url for the closest derivative |
| 194 | + return $srcFile->getThumbUrl( key( $derivativeSet ) . '.ogv'); |
| 195 | + } |
| 196 | +} |
| 197 | + |
| 198 | + |
| 199 | + |
| 200 | +?> |
\ No newline at end of file |