1 /* $Id: work.c,v 1.43 2005/03/17 16:38:49 titer Exp $
3 This file is part of the HandBrake source code.
4 Homepage: <http://handbrake.m0k.org/>.
5 It may be used under the terms of the GNU General Public License. */
18 static void work_func();
19 static void do_job( hb_job_t *, int cpu_count );
20 static void work_loop( void * );
23 * Allocates work object and launches work thread with work_func.
24 * @param jobs Handle to hb_list_t.
25 * @param cpu_count Humber of CPUs found in system.
26 * @param die Handle to user inititated exit indicator.
27 * @param error Handle to error indicator.
29 hb_thread_t * hb_work_init( hb_list_t * jobs, int cpu_count,
30 volatile int * die, int * error )
32 hb_work_t * work = calloc( sizeof( hb_work_t ), 1 );
35 work->cpu_count = cpu_count;
39 return hb_thread_init( "work", work_func, work, HB_LOW_PRIORITY );
43 * Iterates through job list and calls do_job for each job.
44 * @param _work Handle work object.
46 static void work_func( void * _work )
48 hb_work_t * work = _work;
51 hb_log( "%d job(s) to process", hb_list_count( work->jobs ) );
53 while( !*work->die && ( job = hb_list_item( work->jobs, 0 ) ) )
55 hb_list_rem( work->jobs, job );
57 do_job( job, work->cpu_count );
60 *(work->error) = HB_ERROR_NONE;
65 static hb_work_object_t * getWork( int id )
68 for( w = hb_objects; w; w = w->next )
79 * Job initialization rountine.
81 * Creates work objects for synchronizer, video decoder, video renderer, video decoder, audio decoder, audio encoder, reader, muxer.
82 * Launches thread for each work object with work_loop.
83 * Loops while monitoring status of work threads and fifos.
84 * Exits loop when conversion is done and fifos are empty.
85 * Closes threads and frees fifos.
86 * @param job Handle work hb_job_t.
87 * @param cpu_count number of CPUs found in system.
89 static void do_job( hb_job_t * job, int cpu_count )
95 /* FIXME: This feels really hackish, anything better? */
96 hb_work_object_t * audio_w = NULL;
99 hb_subtitle_t * subtitle;
104 job->list_work = hb_list_init();
106 hb_log( "starting job" );
107 hb_log( " + device %s", title->dvd );
108 hb_log( " + title %d, chapter(s) %d to %d", title->index,
109 job->chapter_start, job->chapter_end );
110 if ( job->pixel_ratio == 1 )
112 /* Correct the geometry of the output movie when using PixelRatio */
113 job->height=title->height-job->crop[0]-job->crop[1];
114 job->width=title->width-job->crop[2]-job->crop[3];
117 /* Keep width and height within these boundaries */
118 if (job->maxHeight && (job->height > job->maxHeight) )
120 job->height = job->maxHeight;
121 hb_fix_aspect( job, HB_KEEP_HEIGHT );
122 hb_log("Height out of bounds, scaling down to %i", job->maxHeight);
123 hb_log("New dimensions %i * %i", job->width, job->height);
125 if (job->maxWidth && (job->width > job->maxWidth) )
127 job->width = job->maxWidth;
128 hb_fix_aspect( job, HB_KEEP_WIDTH );
129 hb_log("Width out of bounds, scaling down to %i", job->maxWidth);
130 hb_log("New dimensions %i * %i", job->width, job->height);
133 hb_log( " + %dx%d -> %dx%d, crop %d/%d/%d/%d",
134 title->width, title->height, job->width, job->height,
135 job->crop[0], job->crop[1], job->crop[2], job->crop[3] );
136 hb_log( " + deinterlace %s", job->deinterlace ? "on" : "off" );
137 hb_log( " + grayscale %s", job->grayscale ? "on" : "off" );
138 if( job->vquality >= 0.0 && job->vquality <= 1.0 )
140 hb_log( " + %.3f fps, video quality %.2f", (float) job->vrate /
141 (float) job->vrate_base, job->vquality );
145 hb_log( " + %.3f fps, video bitrate %d kbps, pass %d",
146 (float) job->vrate / (float) job->vrate_base,
147 job->vbitrate, job->pass );
149 hb_log (" + PixelRatio: %d, width:%d, height: %d",job->pixel_ratio,job->width, job->height);
150 job->fifo_mpeg2 = hb_fifo_init( 2048 );
151 job->fifo_raw = hb_fifo_init( 8 );
152 job->fifo_sync = hb_fifo_init( 8 );
153 job->fifo_render = hb_fifo_init( 8 );
154 job->fifo_mpeg4 = hb_fifo_init( 8 );
156 /* Synchronization */
157 hb_list_add( job->list_work, ( w = getWork( WORK_SYNC ) ) );
162 hb_list_add( job->list_work, ( w = getWork( WORK_DECMPEG2 ) ) );
163 w->fifo_in = job->fifo_mpeg2;
164 w->fifo_out = job->fifo_raw;
167 hb_list_add( job->list_work, ( w = getWork( WORK_RENDER ) ) );
168 w->fifo_in = job->fifo_sync;
169 w->fifo_out = job->fifo_render;
172 switch( job->vcodec )
174 case HB_VCODEC_FFMPEG:
175 hb_log( " + encoder FFmpeg" );
176 w = getWork( WORK_ENCAVCODEC );
179 hb_log( " + encoder XviD" );
180 w = getWork( WORK_ENCXVID );
183 hb_log( " + encoder x264" );
184 w = getWork( WORK_ENCX264 );
187 w->fifo_in = job->fifo_render;
188 w->fifo_out = job->fifo_mpeg4;
189 w->config = &job->config;
190 hb_list_add( job->list_work, w );
192 subtitle = hb_list_item( title->list_subtitle, 0 );
195 hb_log( " + subtitle %x, %s", subtitle->id, subtitle->lang );
197 subtitle->fifo_in = hb_fifo_init( 8 );
198 subtitle->fifo_raw = hb_fifo_init( 8 );
200 hb_list_add( job->list_work, ( w = getWork( WORK_DECSUB ) ) );
201 w->fifo_in = subtitle->fifo_in;
202 w->fifo_out = subtitle->fifo_raw;
205 if( job->acodec & HB_ACODEC_AC3 )
207 hb_log( " + audio AC3 passthrough" );
211 hb_log( " + audio %d kbps, %d Hz", job->abitrate, job->arate );
212 hb_log( " + encoder %s", ( job->acodec & HB_ACODEC_FAAC ) ?
213 "faac" : ( ( job->acodec & HB_ACODEC_LAME ) ? "lame" :
217 for( i = 0; i < hb_list_count( title->list_audio ); i++ )
219 audio = hb_list_item( title->list_audio, i );
220 hb_log( " + %x, %s", audio->id, audio->lang );
222 audio->fifo_in = hb_fifo_init( 2048 );
223 audio->fifo_raw = hb_fifo_init( 8 );
224 audio->fifo_sync = hb_fifo_init( 8 );
225 audio->fifo_out = hb_fifo_init( 8 );
227 switch( audio->codec )
230 w = getWork( WORK_DECA52 );
233 w = getWork( WORK_DECAVCODEC );
236 w = getWork( WORK_DECLPCM );
239 w->fifo_in = audio->fifo_in;
240 w->fifo_out = audio->fifo_raw;
241 w->config = &audio->config;
243 /* FIXME: This feels really hackish, anything better? */
244 audio_w = calloc( sizeof( hb_work_object_t ), 1 );
245 audio_w = memcpy( audio_w, w, sizeof( hb_work_object_t ));
247 hb_list_add( job->list_work, audio_w );
249 switch( job->acodec )
252 w = getWork( WORK_ENCFAAC );
255 w = getWork( WORK_ENCLAME );
257 case HB_ACODEC_VORBIS:
258 w = getWork( WORK_ENCVORBIS );
261 if( job->acodec != HB_ACODEC_AC3 )
263 w->fifo_in = audio->fifo_sync;
264 w->fifo_out = audio->fifo_out;
265 w->config = &audio->config;
267 /* FIXME: This feels really hackish, anything better? */
268 audio_w = calloc( sizeof( hb_work_object_t ), 1 );
269 audio_w = memcpy( audio_w, w, sizeof( hb_work_object_t ));
271 hb_list_add( job->list_work, audio_w );
274 /* store this audio's channel counts in the audio struct */
276 /* we will only end up with a channelsused value other than 2
277 if we are encoding to AAC. All other audio encodings will get
280 if (audio->channels == 5 && audio->lfechannels == 1) {
281 /* we have a 5.1 AC-3 source soundtrack */
282 if ((job->acodec == HB_ACODEC_FAAC || job->acodec == HB_ACODEC_VORBIS) && job->surround) {
283 /* we're going to be encoding to AAC,
284 and have turned on the "preserve 5.1" flag */
285 audio->channelsused = 6;
287 /* mix 5.1 down to Dolby Digital (2-channel) for
288 non-AAC audio, or for AAC without 5.1 preservation */
289 audio->channelsused = 2;
291 } else if (audio->channels == 1 && audio->lfechannels == 0) {
292 /* we have a 1.0 mono AC-3 source soundtrack */
293 if (job->acodec == HB_ACODEC_FAAC || job->acodec == HB_ACODEC_VORBIS) {
294 /* we're going to be encoding to AAC,
295 so mix down to a mono AAC track */
296 audio->channelsused = 1;
298 /* mix up the mono track to stereo for non-AAC formats */
299 audio->channelsused = 2;
302 /* mix everything else down to stereo */
303 /* dolby pro-logic will be preserved in deca52.c if necessary
304 by referring to the value of audio->config->a52.ac3flags */
305 audio->channelsused = 2;
308 audio->config.aac.channelsused = audio->config.a52.channelsused = audio->config.vorbis.channelsused = audio->channelsused;
309 audio->config.vorbis.language = audio->lang_simple;
313 /* Init read & write threads */
314 job->reader = hb_reader_init( job );
316 hb_log( " + output: %s", job->file );
317 job->muxer = hb_muxer_init( job );
321 /* Launch processing threads */
322 for( i = 1; i < hb_list_count( job->list_work ); i++ )
324 w = hb_list_item( job->list_work, i );
325 w->done = &job->done;
326 w->thread_sleep_interval = 10;
328 w->thread = hb_thread_init( w->name, work_loop, w,
333 w = hb_list_item( job->list_work, 0 );
334 w->thread_sleep_interval = 50;
338 if( w->work( w, NULL, NULL ) == HB_WORK_DONE )
343 !hb_fifo_size( job->fifo_sync ) &&
344 !hb_fifo_size( job->fifo_render ) &&
345 hb_fifo_size( job->fifo_mpeg4 ) < 2 )
349 hb_snooze( w->thread_sleep_interval );
351 hb_list_rem( job->list_work, w );
355 /* Close work objects */
356 while( ( w = hb_list_item( job->list_work, 0 ) ) )
358 hb_list_rem( job->list_work, w );
359 hb_thread_close( &w->thread );
362 /* FIXME: This feels really hackish, anything better? */
363 if ( w->id == WORK_DECA52 ||
364 w->id == WORK_DECLPCM ||
365 w->id == WORK_ENCFAAC ||
366 w->id == WORK_ENCLAME ||
367 w->id == WORK_ENCVORBIS )
374 /* Stop read & write threads */
375 hb_thread_close( &job->reader );
376 hb_thread_close( &job->muxer );
379 hb_fifo_close( &job->fifo_mpeg2 );
380 hb_fifo_close( &job->fifo_raw );
381 hb_fifo_close( &job->fifo_sync );
382 hb_fifo_close( &job->fifo_render );
383 hb_fifo_close( &job->fifo_mpeg4 );
386 hb_fifo_close( &subtitle->fifo_in );
387 hb_fifo_close( &subtitle->fifo_raw );
389 for( i = 0; i < hb_list_count( title->list_audio ); i++ )
391 audio = hb_list_item( title->list_audio, i );
392 hb_fifo_close( &audio->fifo_in );
393 hb_fifo_close( &audio->fifo_raw );
394 hb_fifo_close( &audio->fifo_sync );
395 hb_fifo_close( &audio->fifo_out );
400 * Performs the work objects specific work function.
401 * Loops calling work function for associated work object. Sleeps when fifo is full.
402 * Monitors work done indicator.
403 * Exits loop when work indiactor is set.
404 * @param _w Handle to work object.
406 static void work_loop( void * _w )
408 hb_work_object_t * w = _w;
409 hb_buffer_t * buf_in, * buf_out;
414 hb_lock( job->pause );
415 hb_unlock( job->pause );
417 if( hb_fifo_is_full( w->fifo_out ) ||
418 // if( (hb_fifo_percent_full( w->fifo_out ) > 0.8) ||
419 !( buf_in = hb_fifo_get( w->fifo_in ) ) )
421 hb_snooze( w->thread_sleep_interval );
422 // w->thread_sleep_interval += 1;
425 // w->thread_sleep_interval = MAX(1, (w->thread_sleep_interval - 1));
427 w->work( w, &buf_in, &buf_out );
430 hb_buffer_close( &buf_in );
434 hb_fifo_push( w->fifo_out, buf_out );