Index: trunk/extensions/OggHandler/patch-image-index.sql |
— | — | @@ -1,14 +0,0 @@ |
2 | | -CREATE INDEX /*i*/image_media_type ON /*_*/image ( img_media_type ); |
3 | | - |
4 | | -CREATE INDEX /*i*/transcode_job_name ON /*_*/transcode_job ( tjob_name ); |
5 | | - |
6 | | -CREATE INDEX /*i*/transcode_job_state ON /*_*/transcode_job ( tjob_state ); |
\ No newline at end of file |
Index: trunk/extensions/OggHandler/OggTranscodeCron.php |
— | — | @@ -1,318 +0,0 @@ |
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__) . '/../../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/OggTranscode.php |
— | — | @@ -1,199 +0,0 @@ |
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 = '200_200kbs'; |
22 | | - const ENC_WEB_4MBS = '360_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' => '360', |
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' => 7, |
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 |
Index: trunk/extensions/OggHandler/OggTranscode.sql |
— | — | @@ -1,29 +0,0 @@ |
2 | | - |
3 | | -CREATE TABLE IF NOT EXISTS /*_*/transcode_job ( |
4 | | - |
5 | | - --File name key ( called image to match table name ) |
6 | | - tjob_name varbinary(255) NOT NULL, |
7 | | - |
8 | | - --Derivative key of the transcode |
9 | | - tjob_derivative_key varbinary(40) NOT NULL, |
10 | | - |
11 | | - -- Timestamp |
12 | | - `tjob_start_timestamp` char(14) NOT NULL, |
13 | | - |
14 | | - -- Transcoding state |
15 | | - tjob_state enum('OK','ASSIGNED','FAILED') default NULL |
16 | | - |
17 | | -) /*$wgDBTableOptions*/; |
18 | | - |
19 | | -CREATE INDEX /*i*/transcode_job_name ON /*_*/transcode_job ( tjob_name ); |
20 | | - |
21 | | -CREATE INDEX /*i*/transcode_job_state ON /*_*/transcode_job ( tjob_state ); |
Index: trunk/extensions/OggHandler/OggTranscode/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 |
Property changes on: trunk/extensions/OggHandler/OggTranscode/patch-image-index.sql |
___________________________________________________________________ |
Name: svn:mergeinfo |
1 | 16 | + |
Index: trunk/extensions/OggHandler/OggTranscode/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 = '200_200kbs'; |
| 22 | + const ENC_WEB_4MBS = '360_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 | + public static $derivativeSettings = array( |
| 35 | + OggTranscode::ENC_WEB_2MBS => |
| 36 | + array( |
| 37 | + 'maxSize' => '200', |
| 38 | + 'videoBitrate' => '128', |
| 39 | + 'audioBitrate' => '32', |
| 40 | + 'samplerate' => '22050', |
| 41 | + 'framerate' => '15', |
| 42 | + 'channels' => '1', |
| 43 | + 'noUpscaling' => 'true', |
| 44 | + 'twopass' => 'true', |
| 45 | + 'keyframeInterval' => '64', |
| 46 | + 'bufDelay' => '128' |
| 47 | + ), |
| 48 | + OggTranscode::ENC_WEB_4MBS => |
| 49 | + array( |
| 50 | + 'maxSize' => '360', |
| 51 | + 'videoBitrate' => '368', |
| 52 | + 'audioBitrate' => '48', |
| 53 | + 'noUpscaling' => 'true', |
| 54 | + 'twopass' => 'true', |
| 55 | + 'keyframeInterval' => '128', |
| 56 | + 'bufDelay' => '256' |
| 57 | + ), |
| 58 | + OggTranscode::ENC_WEB_6MBS => |
| 59 | + array( |
| 60 | + 'maxSize' => '480', |
| 61 | + 'videoBitrate' => '512', |
| 62 | + 'audioBitrate' => '96', |
| 63 | + 'noUpscaling' => 'true', |
| 64 | + 'twopass' => 'true', |
| 65 | + 'keyframeInterval' => '128', |
| 66 | + 'bufDelay' => '256' |
| 67 | + ), |
| 68 | + OggTranscode::ENC_HQ_VBR => |
| 69 | + array( |
| 70 | + 'maxSize' => '720', |
| 71 | + 'videoQuality' => 7, |
| 72 | + 'audioQuality' => 3, |
| 73 | + 'noUpscaling' => 'true' |
| 74 | + ) |
| 75 | + ); |
| 76 | + /** |
| 77 | + * Mapping between firefogg api and ffmpeg2theora command line |
| 78 | + * |
| 79 | + * This lets us share a common api between firefogg and oggTranscode |
| 80 | + * also see: http://firefogg.org/dev/index.html |
| 81 | + */ |
| 82 | + public static $foggMap = array( |
| 83 | + //video |
| 84 | + 'width' => "--width", |
| 85 | + 'height' => "--height", |
| 86 | + 'maxSize' => "--max_size", |
| 87 | + 'noUpscaling' => "--no-upscaling", |
| 88 | + 'videoQuality'=> "-v", |
| 89 | + 'videoBitrate' => "-V", |
| 90 | + 'twopass' => "--two-pass", |
| 91 | + 'framerate' => "-F", |
| 92 | + 'aspect' => "--aspect", |
| 93 | + 'starttime' => "--starttime", |
| 94 | + 'endtime' => "--endtime", |
| 95 | + 'cropTop' => "--croptop", |
| 96 | + 'cropBottom' => "--cropbottom", |
| 97 | + 'cropLeft' => "--cropleft", |
| 98 | + 'cropRight' => "--cropright", |
| 99 | + 'keyframeInterval'=> "--key", |
| 100 | + 'denoise' => array("--pp", "de"), |
| 101 | + 'novideo' => array("--novideo", "--no-skeleton"), |
| 102 | + 'bufDelay' => "--buf-delay", |
| 103 | + //audio |
| 104 | + 'audioQuality' => "-a", |
| 105 | + 'audioBitrate' => "-A", |
| 106 | + 'samplerate' => "-H", |
| 107 | + 'channels' => "-c", |
| 108 | + 'noaudio' => "--noaudio", |
| 109 | + //metadata |
| 110 | + 'artist' => "--artist", |
| 111 | + 'title' => "--title", |
| 112 | + 'date' => "--date", |
| 113 | + 'location' => "--location", |
| 114 | + 'organization' => "--organization", |
| 115 | + 'copyright' => "--copyright", |
| 116 | + 'license' => "--license", |
| 117 | + 'contact' => "--contact" |
| 118 | + ); |
| 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 |
Property changes on: trunk/extensions/OggHandler/OggTranscode/OggTranscode.php |
___________________________________________________________________ |
Name: svn:mergeinfo |
1 | 201 | + |
Index: trunk/extensions/OggHandler/OggTranscode/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__) . '/../../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 |
Property changes on: trunk/extensions/OggHandler/OggTranscode/OggTranscodeCron.php |
___________________________________________________________________ |
Name: svn:mergeinfo |
1 | 320 | + |
Index: trunk/extensions/OggHandler/OggTranscode/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 ); |
Property changes on: trunk/extensions/OggHandler/OggTranscode/OggTranscode.sql |
___________________________________________________________________ |
Name: svn:mergeinfo |
1 | 31 | + |
Index: trunk/extensions/OggHandler/ApiPlayTracking/ApiPlayTracking.sql |
— | — | @@ -0,0 +1,26 @@ |
| 2 | +-- |
| 3 | +-- Table structure for table `play_tracking` |
| 4 | +-- |
| 5 | + |
| 6 | +CREATE TABLE IF NOT EXISTS /*_*/play_tracking ( |
| 7 | + |
| 8 | + -- Unique id of play tracking event |
| 9 | + track_id int unsigned NOT NULL PRIMARY KEY AUTO_INCREMENT |
| 10 | + |
| 11 | + -- Filename of resource played |
| 12 | + track_filename varchar(255) binary NOT NULL, |
| 13 | + |
| 14 | + -- Anonymous hash of client |
| 15 | + track_client_hash varbinary(16) NOT NULL, |
| 16 | + |
| 17 | + -- Browser and playback system dump. |
| 18 | + track_clientplayer tinyblob NOT NULL, |
| 19 | + |
| 20 | + -- Rate we are tracking ( ie 10 means 1 in 10 tracked ) |
| 21 | + track_rate integer |
| 22 | + |
| 23 | +) /*$wgDBTableOptions*/; |
| 24 | + |
| 25 | + |
| 26 | + |
| 27 | + |
Index: trunk/extensions/OggHandler/ApiPlayTracking/ApiPlayTracking.php |
— | — | @@ -0,0 +1,79 @@ |
| 2 | +<?php |
| 3 | +/** |
| 4 | + * Extend the API for play request tracking |
| 5 | + * |
| 6 | + * @file |
| 7 | + * @ingroup API |
| 8 | + */ |
| 9 | + |
| 10 | +class ApiPlayTracking extends ApiBase { |
| 11 | + |
| 12 | + /** |
| 13 | + * Runs when the API is called with "playtracking", takes in "filename" |
| 14 | + * and "client" serialized data for ogg support analysis |
| 15 | + * |
| 16 | + * @see includes/api/ApiBase#execute() |
| 17 | + */ |
| 18 | + public function execute() { |
| 19 | + global $wgEnablePlayTracking; |
| 20 | + if( ! $wgEnablePlayTracking ){ |
| 21 | + $this->dieUsageMsg( array( 'unknownerror', 'Play tracking is not enabled' ) ); |
| 22 | + } |
| 23 | + $params = $this->extractRequestParams(); |
| 24 | + $this->validateParams( $params ); |
| 25 | + |
| 26 | + // insert into the play_tracking table |
| 27 | + } |
| 28 | + |
| 29 | + /** |
| 30 | + * Required parameter check |
| 31 | + * @param $params params extracted from the POST |
| 32 | + */ |
| 33 | + protected function validateParams( $params ) { |
| 34 | + $required = array( 'filename', 'client' ); |
| 35 | + foreach ( $required as $arg ) { |
| 36 | + if ( !isset( $params[$arg] ) ) { |
| 37 | + $this->dieUsageMsg( array( 'missingparam', $arg ) ); |
| 38 | + } |
| 39 | + } |
| 40 | + } |
| 41 | + |
| 42 | + /** |
| 43 | + * Setup the ApiTracking tables |
| 44 | + */ |
| 45 | + public static function schema() { |
| 46 | + global $wgExtNewTables, $wgExtNewIndexes; |
| 47 | + |
| 48 | + $wgExtNewTables[] = array( |
| 49 | + 'play_tracking', |
| 50 | + dirname( __FILE__ ) . '/ApiPlayTracking.sql' |
| 51 | + ); |
| 52 | + |
| 53 | + return true; |
| 54 | + } |
| 55 | + |
| 56 | + public function getParamDescription() { |
| 57 | + return array( |
| 58 | + 'filename' => 'title of filename played', |
| 59 | + 'client' => 'seralized data about client playback support', |
| 60 | + ); |
| 61 | + } |
| 62 | + |
| 63 | + public function getDescription() { |
| 64 | + return array( |
| 65 | + 'Track user audio and video play requests.' |
| 66 | + ); |
| 67 | + } |
| 68 | + |
| 69 | + public function getAllowedParams() { |
| 70 | + return array( |
| 71 | + 'filename' => null, |
| 72 | + 'client' => null, |
| 73 | + ); |
| 74 | + } |
| 75 | + public function getVersion() { |
| 76 | + return __CLASS__ . ': $Id: ApiPlayTracking.php 59374 2009-11-24 01:06:56Z dale $'; |
| 77 | + } |
| 78 | +} |
| 79 | + |
| 80 | +?> |
\ No newline at end of file |
Index: trunk/extensions/OggHandler/OggHandler.php |
— | — | @@ -7,7 +7,6 @@ |
8 | 8 | |
9 | 9 | $oggDir = dirname(__FILE__); |
10 | 10 | $wgAutoloadClasses['OggHandler'] = "$oggDir/OggHandler_body.php"; |
11 | | -$wgAutoloadClasses['OggTranscode'] = "$oggDir/OggTranscode.php"; |
12 | 11 | |
13 | 12 | $wgMediaHandlers['application/ogg'] = 'OggHandler'; |
14 | 13 | if ( !in_array( 'ogg', $wgFileExtensions ) ) { |
— | — | @@ -36,7 +35,8 @@ |
37 | 36 | // Setup a hook for iframe=true (will strip the interface and only output the player) |
38 | 37 | $wgHooks['ArticleFromTitle'][] = 'OggHandler::iframeOutputHook'; |
39 | 38 | |
40 | | -// Add the oggTranscode schema |
| 39 | +// OggTranscode setup |
| 40 | +$wgAutoloadClasses['OggTranscode'] = "$oggDir/OggTranscode/OggTranscode.php"; |
41 | 41 | $wgHooks['LoadExtensionSchemaUpdates'][] = 'OggTranscode::schema'; |
42 | 42 | |
43 | 43 | $wgExtensionCredits['media'][] = array( |
— | — | @@ -54,9 +54,6 @@ |
55 | 55 | $wgOggVideoTypes = array( 'Theora' ); |
56 | 56 | $wgOggAudioTypes = array( 'Vorbis', 'Speex', 'FLAC' ); |
57 | 57 | |
58 | | -// if wgPlayerStats collection is enabled or not |
59 | | -$wgPlayerStatsCollection = false; |
60 | | - |
61 | 58 | // Output Video tag for player ( video tag is then rewritten to compatible player via mwEmbed ) |
62 | 59 | $wgVideoTagOut = false; |
63 | 60 | |
— | — | @@ -89,6 +86,7 @@ |
90 | 87 | $wgEnableTemporalOggUrls = false; |
91 | 88 | |
92 | 89 | // Enabled derivatives array |
| 90 | +// If set to false no derivatives will be used |
93 | 91 | // |
94 | 92 | // Only derivatives with less width than the |
95 | 93 | // source asset size will be created |
— | — | @@ -106,6 +104,13 @@ |
107 | 105 | OggTranscode::ENC_HQ_VBR |
108 | 106 | ); |
109 | 107 | |
| 108 | +// If play requests should be tracked. |
| 109 | +$wgEnablePlayTracking = false; |
| 110 | + |
| 111 | +// One out of how many requests should be tracked: |
| 112 | +$wgPlayTrackingRate = 10; |
| 113 | + |
| 114 | + |
110 | 115 | // Filename or URL path to the Cortado Java player applet. |
111 | 116 | // |
112 | 117 | // If no path is included, the path to this extension's |
— | — | @@ -122,8 +127,24 @@ |
123 | 128 | |
124 | 129 | /******************* CONFIGURATION ENDS HERE **********************/ |
125 | 130 | |
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. |
| 131 | +// NOTE: normally configuration based code would go into extension setup function |
| 132 | +// This config setups hooks and autoloaders that should happen at |
| 133 | +// initial config time |
| 134 | + |
| 135 | +// Alternatively we could have top level php files that include the |
| 136 | +// following pieces of code, but that would distribute configuration |
| 137 | + |
| 138 | +// Enable play tracking |
| 139 | +if( $wgEnablePlayTracking ){ |
| 140 | + // Add the Api Play Tracking setup |
| 141 | + $wgAutoloadClasses['ApiPlayTracking'] = "$oggDir/ApiPlayTracking/ApiPlayTracking.php"; |
| 142 | + $wgHooks['LoadExtensionSchemaUpdates'][] = 'ApiPlayTracking::schema'; |
| 143 | + |
| 144 | + //Add the api entry point: |
| 145 | + $wgAPIModules['playtracking'] = 'ApiPlayTracking'; |
| 146 | +} |
| 147 | + |
| 148 | +// Enable timed text |
128 | 149 | if( $wgEnableTimedText ){ |
129 | 150 | /** |
130 | 151 | * Handle Adding of "timedText" NameSpace |
Index: trunk/extensions/OggHandler/OggHandler_body.php |
— | — | @@ -452,21 +452,30 @@ |
453 | 453 | |
454 | 454 | function setHeaders( $out ) { |
455 | 455 | global $wgOggScriptVersion, $wgCortadoJarFile, $wgServer, $wgUser, $wgScriptPath, |
456 | | - $wgPlayerStatsCollection, $wgVideoTagOut; |
| 456 | + $wgEnablePlayTracking, $wgPlayTrackingRate, $wgVideoTagOut; |
457 | 457 | |
458 | 458 | if( $wgVideoTagOut ){ |
459 | | - // We could add "video" tag javascript here if want. specifically: |
| 459 | + // We could add "video" tag javascript |
| 460 | + // If we wanted to block on mwEmbed player js, instead of loading the js onDomReady |
460 | 461 | |
461 | | - // <script type="text/javascript" src="js/mwEmbed/jsScriptLoader.php?class=window.jQuery,mwEmbed,$j.ui,mw.EmbedPlayer,nativeEmbed,ctrlBuilder,mvpcfConfig,kskinConfig,$j.fn.menu,$j.cookie,$j.ui.slider,mw.TimedText&debug=true"></script> |
462 | | - //<link rel="stylesheet" href="js/mwEmbed/skins/styles.css" type="text/css" media="screen" /> |
| 462 | + // embedPlayer classes include: $j.ui,mw.EmbedPlayer,nativeEmbed,ctrlBuilder,mvpcfConfig,kskinConfig,$j.fn.menu,$j.cookie,$j.ui.slider,mw.TimedText |
463 | 463 | //<link rel="stylesheet" href="js/mwEmbed/skins/kskin/playerSkin.css" type="text/css" media="screen" /> |
464 | 464 | |
465 | | - // The above is loaded on-dom-ready for faster dom readyness. |
466 | | - // but that has the disadvantage of video player interfaces not being "instantly" ready |
467 | | - // on page load. So its a trade off. |
| 465 | + // Loading dynamically lets us avoid unnecessary code |
| 466 | + // ie firefox does not need "JSON.js" and IE ~maybe~ needs cortado embed etc. |
468 | 467 | |
469 | | - // Loading dynamically also lets us avoid unnecessary code |
470 | | - // ie firefox does not need "JSON.js" and IE ~maybe~ needs cortado embed etc. |
| 468 | + if( $wgEnablePlayTracking ) { |
| 469 | + $encPlayTracking = Xml::encodeJsVar( $wgPlayTrackingRate ); |
| 470 | + // Should replace with a standard way to send configuration to mw core js |
| 471 | + $out->addHeadItem( 'OggHandler', <<<EOT |
| 472 | +<script type="text/javascript"> |
| 473 | +mw.setConfig('playTracking', 'true'); |
| 474 | +mw.setConfig('playTrackingRate', $encPlayTracking ); |
| 475 | +</script> |
| 476 | +EOT |
| 477 | +); |
| 478 | + } |
| 479 | + |
471 | 480 | }else{ |
472 | 481 | if ( $out->hasHeadItem( 'OggHandler' ) ) { |
473 | 482 | return; |
— | — | @@ -491,12 +500,26 @@ |
492 | 501 | $encCortadoUrl = Xml::encodeJsVar( $cortadoUrl ); |
493 | 502 | $encExtPathUrl = Xml::encodeJsVar( $scriptPath ); |
494 | 503 | |
| 504 | + |
| 505 | + $playTrackingJs = ''; |
| 506 | + //Check for play tracking and output vars |
| 507 | + if( $wgEnablePlayTracking ) { |
| 508 | + $encPlayTracking = Xml::encodeJsVar( $wgPlayTrackingRate ); |
| 509 | + $playTrackingJs = <<<EOT |
| 510 | +wgOggPlayer.playTracking = true; |
| 511 | +wgOggPlayer.playTrackingRate = $encPlayTracking |
| 512 | +EOT |
| 513 | +; |
| 514 | + } |
| 515 | + |
| 516 | + |
495 | 517 | $out->addHeadItem( 'OggHandler', <<<EOT |
496 | 518 | <script type="text/javascript" src="$scriptPath/OggPlayer.js?$wgOggScriptVersion"></script> |
497 | 519 | <script type="text/javascript"> |
498 | 520 | wgOggPlayer.msg = $jsMsgs; |
499 | 521 | wgOggPlayer.cortadoUrl = $encCortadoUrl; |
500 | 522 | wgOggPlayer.extPathUrl = $encExtPathUrl; |
| 523 | +$playTrackingJs; |
501 | 524 | </script> |
502 | 525 | <style type="text/css"> |
503 | 526 | .ogg-player-options { |