X-Git-Url: http://git.osdn.jp/view?a=blobdiff_plain;f=libhb%2Fwork.c;h=c1891a6838b53908a0c93a444209da203215d4c2;hb=55b0015a8c50106e553bc2f48336cc2a1c495459;hp=11a52bd027618e49e4d9f4279818cf58126710a6;hpb=7dddefa27398494a3b9d20ab3fd9d824c7bf1003;p=handbrake-jp%2Fhandbrake-jp-git.git diff --git a/libhb/work.c b/libhb/work.c index 11a52bd0..c1891a68 100644 --- a/libhb/work.c +++ b/libhb/work.c @@ -1,12 +1,13 @@ /* $Id: work.c,v 1.43 2005/03/17 16:38:49 titer Exp $ This file is part of the HandBrake source code. - Homepage: . + Homepage: . It may be used under the terms of the GNU General Public License. */ #include "hb.h" #include "a52dec/a52.h" #include "dca.h" +#include "libavformat/avformat.h" typedef struct { @@ -22,7 +23,10 @@ static void work_func(); static void do_job( hb_job_t *, int cpu_count ); static void work_loop( void * ); -#define FIFO_CPU_MULT 8 +#define FIFO_LARGE 32 +#define FIFO_LARGE_WAKE 16 +#define FIFO_SMALL 16 +#define FIFO_SMALL_WAKE 15 /** * Allocates work object and launches work thread with work_func. @@ -45,6 +49,24 @@ hb_thread_t * hb_work_init( hb_list_t * jobs, int cpu_count, return hb_thread_init( "work", work_func, work, HB_LOW_PRIORITY ); } +static void InitWorkState( hb_handle_t * h ) +{ + hb_state_t state; + + state.state = HB_STATE_WORKING; +#define p state.param.working + p.progress = 0.0; + p.rate_cur = 0.0; + p.rate_avg = 0.0; + p.hours = -1; + p.minutes = -1; + p.seconds = -1; +#undef p + + hb_set_state( h, &state ); + +} + /** * Iterates through job list and calls do_job for each job. * @param _work Handle work object. @@ -61,6 +83,7 @@ static void work_func( void * _work ) hb_list_rem( work->jobs, job ); job->die = work->die; *(work->current_job) = job; + InitWorkState( job->h ); do_job( job, work->cpu_count ); *(work->current_job) = NULL; } @@ -70,295 +93,425 @@ static void work_func( void * _work ) free( work ); } -static hb_work_object_t * getWork( int id ) +hb_work_object_t * hb_get_work( int id ) { hb_work_object_t * w; for( w = hb_objects; w; w = w->next ) { if( w->id == id ) { - return w; + hb_work_object_t *wc = malloc( sizeof(*w) ); + *wc = *w; + return wc; } } return NULL; } +hb_work_object_t * hb_codec_decoder( int codec ) +{ + switch( codec ) + { + case HB_ACODEC_AC3: return hb_get_work( WORK_DECA52 ); + case HB_ACODEC_DCA: return hb_get_work( WORK_DECDCA ); + case HB_ACODEC_MPGA: return hb_get_work( WORK_DECAVCODEC ); + case HB_ACODEC_LPCM: return hb_get_work( WORK_DECLPCM ); + case HB_ACODEC_FFMPEG: return hb_get_work( WORK_DECAVCODECAI ); + } + return NULL; +} + +hb_work_object_t * hb_codec_encoder( int codec ) +{ + switch( codec ) + { + case HB_ACODEC_FAAC: return hb_get_work( WORK_ENCFAAC ); + case HB_ACODEC_LAME: return hb_get_work( WORK_ENCLAME ); + case HB_ACODEC_VORBIS: return hb_get_work( WORK_ENCVORBIS ); + case HB_ACODEC_CA_AAC: return hb_get_work( WORK_ENC_CA_AAC ); + } + return NULL; +} + /** - * Job initialization rountine. - * Initializes fifos. - * Creates work objects for synchronizer, video decoder, video renderer, video decoder, audio decoder, audio encoder, reader, muxer. - * Launches thread for each work object with work_loop. - * Loops while monitoring status of work threads and fifos. - * Exits loop when conversion is done and fifos are empty. - * Closes threads and frees fifos. + * Displays job parameters in the debug log. * @param job Handle work hb_job_t. - * @param cpu_count number of CPUs found in system. */ -static void do_job( hb_job_t * job, int cpu_count ) +void hb_display_job_info( hb_job_t * job ) { - hb_title_t * title; - int i, j; - hb_work_object_t * w; - - /* FIXME: This feels really hackish, anything better? */ - hb_work_object_t * audio_w = NULL; - hb_work_object_t * sub_w = NULL; - hb_work_object_t * final_w = NULL; - + hb_title_t * title = job->title; hb_audio_t * audio; hb_subtitle_t * subtitle; - int done; - unsigned int subtitle_highest = 0; - unsigned int subtitle_highest_id = 0; - unsigned int subtitle_lowest = -1; - unsigned int subtitle_lowest_id = 0; - unsigned int subtitle_forced_id = 0; - unsigned int subtitle_hit = 0; - - title = job->title; - - job->list_work = hb_list_init(); + int i, j; + + hb_log("job configuration:"); + hb_log( " * source"); + + hb_log( " + %s", title->path ); - hb_log( "starting job" ); - hb_log( " + device %s", title->dvd ); - hb_log( " + title %d, chapter(s) %d to %d", title->index, + hb_log( " + title %d, chapter(s) %d to %d", title->index, job->chapter_start, job->chapter_end ); - if ( job->pixel_ratio == 1 ) - { - /* Correct the geometry of the output movie when using PixelRatio */ - job->height=title->height-job->crop[0]-job->crop[1]; - job->width=title->width-job->crop[2]-job->crop[3]; - } - else if ( job->pixel_ratio == 2 ) - { - - /* While keeping the DVD storage aspect, resize the job width and height - so they fit into the user's specified dimensions. */ - hb_set_anamorphic_size(job, &job->width, &job->height, &job->pixel_aspect_width, &job->pixel_aspect_height); - } - - /* Keep width and height within these boundaries, - but ignore for "loose" anamorphic encodes, for - which this stuff is covered in the pixel_ratio - section right above.*/ - if (job->maxHeight && (job->height > job->maxHeight) && (job->pixel_ratio != 2)) - { - job->height = job->maxHeight; - hb_fix_aspect( job, HB_KEEP_HEIGHT ); - hb_log("Height out of bounds, scaling down to %i", job->maxHeight); - hb_log("New dimensions %i * %i", job->width, job->height); - } - if (job->maxWidth && (job->width > job->maxWidth) && (job->pixel_ratio != 2)) - { - job->width = job->maxWidth; - hb_fix_aspect( job, HB_KEEP_WIDTH ); - hb_log("Width out of bounds, scaling down to %i", job->maxWidth); - hb_log("New dimensions %i * %i", job->width, job->height); - } - - hb_log( " + %dx%d -> %dx%d, crop %d/%d/%d/%d", - title->width, title->height, job->width, job->height, - job->crop[0], job->crop[1], job->crop[2], job->crop[3] ); - if ( job->grayscale ) - hb_log( " + grayscale mode" ); + if( title->container_name != NULL ) + hb_log( " + container: %s", title->container_name); - if ( job->vfr ) + if( title->data_rate ) { - int detelecine_present = 0; - if ( job->filters ) - { - for( i = 0; i < hb_list_count( job->filters ); i++ ) - { - hb_filter_object_t * filter = hb_list_item( job->filters, i ); - if (filter->id == FILTER_DETELECINE) - detelecine_present = 1; - } - } + hb_log( " + data rate: %d kbps", title->data_rate / 1000 ); + } + + hb_log( " * destination"); - if (!detelecine_present) - { - /* Allocate the filter. */ - hb_filter_object_t * filter = malloc( sizeof( hb_filter_object_t ) ); + hb_log( " + %s", job->file ); - /* Copy in the contents of the detelecine struct. */ - memcpy( filter, &hb_filter_detelecine, sizeof( hb_filter_object_t ) ); + switch( job->mux ) + { + case HB_MUX_MP4: + hb_log(" + container: MPEG-4 (.mp4 and .m4v)"); + + if( job->ipod_atom ) + hb_log( " + compatibility atom for iPod 5G"); + + if( job->largeFileSize ) + hb_log( " + 64-bit formatting"); + + if( job->mp4_optimize ) + hb_log( " + optimized for progressive web downloads"); + + if( job->color_matrix ) + hb_log( " + custom color matrix: %s", job->color_matrix == 1 ? "ITU Bt.601 (SD)" : "ITU Bt.709 (HD)"); + break; - /* Set the name to a copy of the template name so render.c has something to free. */ - filter->name = strdup(hb_filter_detelecine.name); + case HB_MUX_AVI: + hb_log(" + container: AVI"); + break; - /* Add it to the list. */ - hb_list_add( job->filters, filter ); + case HB_MUX_MKV: + hb_log(" + container: Matroska (.mkv)"); + break; - hb_log("work: VFR mode -- adding detelecine filter"); - } + case HB_MUX_OGM: + hb_log(" + conttainer: Ogg Media (.ogm)"); + break; } - if( hb_list_count( job->filters ) ) + if( job->chapter_markers ) { - hb_log(" + filters"); - for( i = 0; i < hb_list_count( job->filters ); i++ ) - { - hb_filter_object_t * filter = hb_list_item( job->filters, i ); - if (filter->settings) - hb_log(" + %s (%s)", filter->name, filter->settings); - else - hb_log(" + %s (default settings)", filter->name); - } + hb_log( " + chapter markers" ); } - - if( job->vfr) + + hb_log(" * video track"); + + hb_log(" + decoder: %s", title->video_codec_name ); + + if( title->video_bitrate ) { - hb_log( " + video frame rate: variable (detected %.3f fps)", (float) job->vrate / - (float) job->vrate_base ); + hb_log( " + bitrate %d kbps", title->video_bitrate / 1000 ); + } + + if( !job->cfr ) + { + hb_log( " + frame rate: same as source (around %.3f fps)", + (float) title->rate / (float) title->rate_base ); } else { - hb_log( " + video frame rate: %.3f fps", (float) job->vrate / (float) job->vrate_base); + static const char *frtypes[] = { + "", "constant", "peak rate limited to" + }; + hb_log( " + frame rate: %.3f fps -> %s %.3f fps", + (float) title->rate / (float) title->rate_base, frtypes[job->cfr], + (float) job->vrate / (float) job->vrate_base ); } - if( job->vquality >= 0.0 && job->vquality <= 1.0 ) + if( job->anamorphic.mode ) { - hb_log( " + video quality %.2f", job->vquality ); + hb_log( " + %s anamorphic", job->anamorphic.mode == 1 ? "strict" : job->anamorphic.mode == 2? "loose" : "custom" ); + if( job->anamorphic.mode == 3 && job->anamorphic.keep_display_aspect ) + { + hb_log( " + keeping source display aspect ratio"); + } + hb_log( " + storage dimensions: %d * %d -> %d * %d, crop %d/%d/%d/%d, mod %i", + title->width, title->height, job->width, job->height, + job->crop[0], job->crop[1], job->crop[2], job->crop[3], job->modulus ); + if( job->anamorphic.itu_par ) + { + hb_log( " + using ITU pixel aspect ratio values"); + } + hb_log( " + pixel aspect ratio: %i / %i", job->anamorphic.par_width, job->anamorphic.par_height ); + hb_log( " + display dimensions: %.0f * %i", + (float)( job->width * job->anamorphic.par_width / job->anamorphic.par_height ), job->height ); } else { - hb_log( " + video bitrate %d kbps, pass %d", job->vbitrate, job->pass ); + hb_log( " + dimensions: %d * %d -> %d * %d, crop %d/%d/%d/%d", + title->width, title->height, job->width, job->height, + job->crop[0], job->crop[1], job->crop[2], job->crop[3] ); } - hb_log (" + PixelRatio: %d, width:%d, height: %d",job->pixel_ratio,job->width, job->height); - job->fifo_mpeg2 = hb_fifo_init( 2048 ); - job->fifo_raw = hb_fifo_init( FIFO_CPU_MULT * cpu_count ); - job->fifo_sync = hb_fifo_init( FIFO_CPU_MULT * cpu_count ); - job->fifo_render = hb_fifo_init( FIFO_CPU_MULT * cpu_count ); - job->fifo_mpeg4 = hb_fifo_init( FIFO_CPU_MULT * cpu_count ); - - /* Synchronization */ - hb_list_add( job->list_work, ( w = getWork( WORK_SYNC ) ) ); - w->fifo_in = NULL; - w->fifo_out = NULL; - - /* Video decoder */ - hb_list_add( job->list_work, ( w = getWork( WORK_DECMPEG2 ) ) ); - w->fifo_in = job->fifo_mpeg2; - w->fifo_out = job->fifo_raw; + if ( job->grayscale ) + hb_log( " + grayscale mode" ); - /* Video renderer */ - hb_list_add( job->list_work, ( w = getWork( WORK_RENDER ) ) ); - w->fifo_in = job->fifo_sync; - w->fifo_out = job->fifo_render; - if ( job->indepth_scan ) + if( hb_list_count( job->filters ) ) { - /* - * if we're doing a subtitle scan the last thread in the - * processing pipeline is render - remember it so we can - * wait for its completion below. - */ - final_w = w; + hb_log(" + %s", hb_list_count( job->filters) > 1 ? "filters" : "filter" ); + for( i = 0; i < hb_list_count( job->filters ); i++ ) + { + hb_filter_object_t * filter = hb_list_item( job->filters, i ); + if (filter->settings) + hb_log(" + %s (%s)", filter->name, filter->settings); + else + hb_log(" + %s (default settings)", filter->name); + } } - - /* Video encoder */ - switch( job->vcodec ) + + if( !job->indepth_scan ) { - case HB_VCODEC_FFMPEG: - hb_log( " + encoder FFmpeg" ); - w = getWork( WORK_ENCAVCODEC ); - break; - case HB_VCODEC_XVID: - hb_log( " + encoder XviD" ); - w = getWork( WORK_ENCXVID ); - break; - case HB_VCODEC_X264: - hb_log( " + encoder x264" ); - if( job->x264opts != NULL && *job->x264opts != '\0' ) - hb_log( " + x264 options: %s", job->x264opts); - w = getWork( WORK_ENCX264 ); - break; - case HB_VCODEC_THEORA: - hb_log( " + encoder Theora" ); - w = getWork( WORK_ENCTHEORA ); - break; - } - w->fifo_in = job->fifo_render; - w->fifo_out = job->fifo_mpeg4; - w->config = &job->config; + /* Video encoder */ + switch( job->vcodec ) + { + case HB_VCODEC_FFMPEG: + hb_log( " + encoder: FFmpeg" ); + break; - hb_list_add( job->list_work, w ); - if ( !job->indepth_scan ) - { - /* - * if we're not doing a subtitle scan the last thread in the - * processing pipeline is the encoder - remember it so we can - * wait for its completion below. - */ - final_w = w; - } + case HB_VCODEC_X264: + hb_log( " + encoder: x264" ); + if( job->x264opts != NULL && *job->x264opts != '\0' ) + hb_log( " + options: %s", job->x264opts); + break; - if( job->select_subtitle && !job->indepth_scan ) - { - /* - * Must be second pass of a two pass with subtitle scan enabled, so - * add the subtitle that we found on the first pass for use in this - * pass. - */ - if (*(job->select_subtitle)) + case HB_VCODEC_THEORA: + hb_log( " + encoder: Theora" ); + break; + } + + if( job->vquality >= 0.0 && job->vquality <= 1.0 ) + { + hb_log( " + quality: %.2f", job->vquality ); + } + else if( job->vquality > 1 ) + { + hb_log( " + quality: %.2f %s", job->vquality, job->vcodec == HB_VCODEC_X264 ? "(RF)" : "(QP)" ); + } + else { - hb_list_add( title->list_subtitle, *( job->select_subtitle ) ); + hb_log( " + bitrate: %d kbps, pass: %d", job->vbitrate, job->pass ); } } - for( i=0; i < hb_list_count(title->list_subtitle); i++ ) + for( i=0; i < hb_list_count( title->list_subtitle ); i++ ) { subtitle = hb_list_item( title->list_subtitle, i ); if( subtitle ) { - hb_log( " + subtitle %x, %s", subtitle->id, subtitle->lang ); + if( subtitle->source == SRTSUB ) + { + /* For SRT, print offset and charset too */ + hb_log( " * subtitle track %i, %s (id %x) %s [%s] -> %s%s, offset: %"PRId64", charset: %s", + subtitle->track, subtitle->lang, subtitle->id, "Text", "SRT", "Pass-Through", + subtitle->config.default_track ? ", Default" : "", + subtitle->config.offset, subtitle->config.src_codeset ); + } + else + { + hb_log( " * subtitle track %i, %s (id %x) %s [%s] -> %s%s%s", subtitle->track, subtitle->lang, subtitle->id, + subtitle->format == PICTURESUB ? "Picture" : "Text", + subtitle->source == VOBSUB ? "VOBSUB" : + subtitle->source == CC608SUB || subtitle->source == CC708SUB ? "CC" : + subtitle->source == UTF8SUB ? "UTF-8" : + subtitle->source == TX3GSUB ? "TX3G" : + subtitle->source == SSASUB ? "SSA" : "Unknown", + subtitle->config.dest == RENDERSUB ? "Render/Burn in" : "Pass-Through", + subtitle->config.force ? ", Forced Only" : "", + subtitle->config.default_track ? ", Default" : "" ); + } + } + } + + if( !job->indepth_scan ) + { + for( i = 0; i < hb_list_count( title->list_audio ); i++ ) + { + audio = hb_list_item( title->list_audio, i ); - subtitle->fifo_in = hb_fifo_init( FIFO_CPU_MULT * cpu_count ); - subtitle->fifo_raw = hb_fifo_init( FIFO_CPU_MULT * cpu_count ); + hb_log( " * audio track %d", audio->config.out.track ); + + if( audio->config.out.name ) + hb_log( " + name: %s", audio->config.out.name ); - /* - * Disable forced subtitles if we didn't find any in the scan - * so that we display normal subtitles instead. - * - * select_subtitle implies that we did a scan. - */ - if( !job->indepth_scan && job->subtitle_force && - job->select_subtitle ) + hb_log( " + decoder: %s (track %d, id %x)", audio->config.lang.description, audio->config.in.track + 1, audio->id ); + + if( ( audio->config.in.codec == HB_ACODEC_AC3 ) || ( audio->config.in.codec == HB_ACODEC_DCA) ) { - if( subtitle->forced_hits == 0 ) - { - job->subtitle_force = 0; - } + hb_log( " + bitrate: %d kbps, samplerate: %d Hz", audio->config.in.bitrate / 1000, audio->config.in.samplerate ); } - if (!job->indepth_scan || job->subtitle_force) { - /* - * Don't add threads for subtitles when we are scanning, unless - * looking for forced subtitles. - */ - if( sub_w != NULL ) + if( (audio->config.out.codec != HB_ACODEC_AC3) && (audio->config.out.codec != HB_ACODEC_DCA) ) + { + for (j = 0; j < hb_audio_mixdowns_count; j++) { - /* - * Need to copy the prior subtitle structure so that we - * don't overwrite the fifos. - */ - sub_w = calloc( sizeof( hb_work_object_t ), 1 ); - sub_w = memcpy( sub_w, w, sizeof( hb_work_object_t )); - } else { - w = sub_w = getWork( WORK_DECSUB ); + if (hb_audio_mixdowns[j].amixdown == audio->config.out.mixdown) { + hb_log( " + mixdown: %s", hb_audio_mixdowns[j].human_readable_name ); + break; + } } - hb_list_add( job->list_work, sub_w ); - sub_w->fifo_in = subtitle->fifo_in; - sub_w->fifo_out = subtitle->fifo_raw; + } + + if ( audio->config.out.dynamic_range_compression && (audio->config.out.codec != HB_ACODEC_AC3) && (audio->config.out.codec != HB_ACODEC_DCA)) + { + hb_log(" + dynamic range compression: %f", audio->config.out.dynamic_range_compression); + } + + if( (audio->config.out.codec == HB_ACODEC_AC3) || (audio->config.out.codec == HB_ACODEC_DCA) ) + { + hb_log( " + %s passthrough", (audio->config.out.codec == HB_ACODEC_AC3) ? + "AC3" : "DCA" ); + } + else + { + hb_log( " + encoder: %s", ( audio->config.out.codec == HB_ACODEC_FAAC ) ? + "faac" : ( ( audio->config.out.codec == HB_ACODEC_LAME ) ? + "lame" : ( ( audio->config.out.codec == HB_ACODEC_CA_AAC ) ? + "ca_aac" : "vorbis" ) ) ); + hb_log( " + bitrate: %d kbps, samplerate: %d Hz", audio->config.out.bitrate, audio->config.out.samplerate ); } } } +} + +/* Corrects framerates when actual duration and frame count numbers are known. */ +void correct_framerate( hb_job_t * job ) +{ + int real_frames; - /* if we are doing passthru, and the input codec is not the same as the output - * codec, then remove this audio from the job */ - /* otherwise, Bad Things will happen */ + hb_interjob_t * interjob = hb_interjob_get( job->h ); + + if( ( job->sequence_id & 0xFFFFFF ) != ( interjob->last_job & 0xFFFFFF) ) + return; // Interjob information is for a different encode. + + /* Cache the original framerate before altering it. */ + interjob->vrate = job->vrate; + interjob->vrate_base = job->vrate_base; + + real_frames = interjob->frame_count - interjob->render_dropped; + + job->vrate = job->vrate_base * ( (double)real_frames * 90000 / interjob->total_time ); +} + +/** + * Job initialization rountine. + * Initializes fifos. + * Creates work objects for synchronizer, video decoder, video renderer, video decoder, audio decoder, audio encoder, reader, muxer. + * Launches thread for each work object with work_loop. + * Loops while monitoring status of work threads and fifos. + * Exits loop when conversion is done and fifos are empty. + * Closes threads and frees fifos. + * @param job Handle work hb_job_t. + * @param cpu_count number of CPUs found in system. + */ +static void do_job( hb_job_t * job, int cpu_count ) +{ + hb_title_t * title; + int i, j; + hb_work_object_t * w; + hb_work_object_t * sync; + hb_work_object_t * muxer; + hb_interjob_t * interjob; + + hb_audio_t * audio; + hb_subtitle_t * subtitle; + unsigned int subtitle_highest = 0; + unsigned int subtitle_highest_id = 0; + unsigned int subtitle_lowest = -1; + unsigned int subtitle_lowest_id = 0; + unsigned int subtitle_forced_id = 0; + unsigned int subtitle_hit = 0; + + title = job->title; + interjob = hb_interjob_get( job->h ); + + if( job->pass == 2 && !job->cfr ) + { + correct_framerate( job ); + } + + job->list_work = hb_list_init(); + + hb_log( "starting job" ); + + if( job->anamorphic.mode ) + { + hb_set_anamorphic_size(job, &job->width, &job->height, &job->anamorphic.par_width, &job->anamorphic.par_height); + + if( job->vcodec == HB_VCODEC_FFMPEG ) + { + /* Just to make working with ffmpeg even more fun, + lavc's MPEG-4 encoder can't handle PAR values >= 255, + even though AVRational does. Adjusting downwards + distorts the display aspect slightly, but such is life. */ + while ((job->anamorphic.par_width & ~0xFF) || + (job->anamorphic.par_height & ~0xFF)) + { + job->anamorphic.par_width >>= 1; + job->anamorphic.par_height >>= 1; + } + } + } + + /* Keep width and height within these boundaries, + but ignore for anamorphic. For "loose" anamorphic encodes, + this stuff is covered in the pixel_ratio section above. */ + if ( job->maxHeight && ( job->height > job->maxHeight ) && ( !job->anamorphic.mode ) ) + { + job->height = job->maxHeight; + hb_fix_aspect( job, HB_KEEP_HEIGHT ); + hb_log( "Height out of bounds, scaling down to %i", job->maxHeight ); + hb_log( "New dimensions %i * %i", job->width, job->height ); + } + if ( job->maxWidth && ( job->width > job->maxWidth ) && ( !job->anamorphic.mode ) ) + { + job->width = job->maxWidth; + hb_fix_aspect( job, HB_KEEP_WIDTH ); + hb_log( "Width out of bounds, scaling down to %i", job->maxWidth ); + hb_log( "New dimensions %i * %i", job->width, job->height ); + } + + if( job->mux & HB_MUX_AVI ) + { + // The concept of variable frame rate video was a bit too advanced + // for Microsoft so AVI doesn't support it. Since almost all dvd + // video is VFR we have to convert it to constant frame rate to + // put it in an AVI container. So duplicate, drop and + // otherwise trash video frames to appease the gods of Redmond. + job->cfr = 1; + } + + if ( job->cfr == 0 ) + { + /* Ensure we're using "Same as source" FPS */ + job->vrate_base = title->rate_base; + } + + job->fifo_mpeg2 = hb_fifo_init( FIFO_LARGE, FIFO_LARGE_WAKE ); + job->fifo_raw = hb_fifo_init( FIFO_SMALL, FIFO_SMALL_WAKE ); + job->fifo_sync = hb_fifo_init( FIFO_SMALL, FIFO_SMALL_WAKE ); + job->fifo_render = hb_fifo_init( FIFO_SMALL, FIFO_SMALL_WAKE ); + job->fifo_mpeg4 = hb_fifo_init( FIFO_LARGE, FIFO_LARGE_WAKE ); + + /* + * Audio fifos must be initialized before sync + */ + if( !job->indepth_scan ) + { + // if we are doing passthru, and the input codec is not the same as the output + // codec, then remove this audio from the job. If we're not doing passthru and + // the input codec is the 'internal' ffmpeg codec, make sure that only one + // audio references that audio stream since the codec context is specific to + // the audio id & multiple copies of the same stream will garble the audio + // or cause aborts. + uint8_t aud_id_uses[MAX_STREAMS]; + memset( aud_id_uses, 0, sizeof(aud_id_uses) ); for( i = 0; i < hb_list_count( title->list_audio ); ) { audio = hb_list_item( title->list_audio, i ); @@ -371,314 +524,497 @@ static void do_job( hb_job_t * job, int cpu_count ) free( audio ); continue; } + if ( audio->config.in.codec == HB_ACODEC_FFMPEG ) + { + if ( aud_id_uses[audio->id] ) + { + hb_log( "Multiple decodes of audio id %d, removing track %d", + audio->id, audio->config.out.track ); + hb_list_rem( title->list_audio, audio ); + free( audio ); + continue; + } + ++aud_id_uses[audio->id]; + } /* Adjust output track number, in case we removed one. * Output tracks sadly still need to be in sequential order. */ audio->config.out.track = i++; } + int requested_mixdown = 0; + int requested_mixdown_index = 0; + for( i = 0; i < hb_list_count( title->list_audio ); i++ ) { audio = hb_list_item( title->list_audio, i ); - hb_log( " + audio track %d", audio->config.out.track ); - hb_log( " + input track %d", audio->config.in.track ); - if( (audio->config.out.codec == HB_ACODEC_AC3) || (audio->config.out.codec == HB_ACODEC_DCA) ) - { - hb_log( " + %s passthrough", (audio->config.out.codec == HB_ACODEC_AC3) ? - "AC3" : "DCA" ); - } - else - { - hb_log( " + audio %d kbps, %d Hz", audio->config.out.bitrate, audio->config.out.samplerate ); - hb_log( " + encoder %s", ( audio->config.out.codec == HB_ACODEC_FAAC ) ? - "faac" : ( ( audio->config.out.codec == HB_ACODEC_LAME ) ? "lame" : - "vorbis" ) ); - if ( audio->config.out.dynamic_range_compression > 1 ) - hb_log(" + dynamic range compression: %f", audio->config.out.dynamic_range_compression); - } - - hb_log( " + %x, %s", audio->id, audio->config.lang.description ); - /* sense-check the current mixdown options */ + if( audio->config.out.codec != audio->config.in.codec ) + { + /* sense-check the current mixdown options */ - /* log the requested mixdown */ - for (j = 0; j < hb_audio_mixdowns_count; j++) { - if (hb_audio_mixdowns[j].amixdown == audio->config.out.mixdown) { - hb_log( " + Requested mixdown: %s (%s)", hb_audio_mixdowns[j].human_readable_name, hb_audio_mixdowns[j].internal_name ); - break; - } - } + /* log the requested mixdown */ + for (j = 0; j < hb_audio_mixdowns_count; j++) { + if (hb_audio_mixdowns[j].amixdown == audio->config.out.mixdown) { + requested_mixdown = audio->config.out.mixdown; + requested_mixdown_index = j; + } + } - /* sense-check the requested mixdown */ + /* sense-check the requested mixdown */ - /* audioCodecsSupportMono and audioCodecsSupport6Ch are the same for now, - but this may change in the future, so they are separated for flexibility */ - int audioCodecsSupportMono = ( (audio->config.in.codec == HB_ACODEC_AC3 || audio->config.in.codec == HB_ACODEC_DCA) && - (audio->config.out.codec == HB_ACODEC_FAAC || audio->config.out.codec == HB_ACODEC_VORBIS) ); - int audioCodecsSupport6Ch = ( (audio->config.in.codec == HB_ACODEC_AC3 || audio->config.in.codec == HB_ACODEC_DCA) && - (audio->config.out.codec == HB_ACODEC_FAAC || audio->config.out.codec == HB_ACODEC_VORBIS)); + if( audio->config.out.mixdown == 0 && + audio->config.out.codec != HB_ACODEC_AC3 && + audio->config.out.codec != HB_ACODEC_DCA ) + { + /* + * Mixdown wasn't specified and this is not pass-through, + * set a default mixdown of stereo. + */ + audio->config.out.mixdown = HB_AMIXDOWN_STEREO; + } - /* find out what the format of our source audio is */ - switch (audio->config.in.channel_layout & HB_INPUT_CH_LAYOUT_DISCRETE_NO_LFE_MASK) { + // Here we try to sanitize the audio input to output mapping. + // Constraints are: + // 1. only the AC3 & DCA decoder libraries currently support mixdown + // 2. the lame encoder library only supports stereo. + // So if the encoder is lame we need the output to be stereo (or multichannel + // matrixed into stereo like dpl). If the decoder is not AC3 or DCA the + // encoder has to handle the input format since we can't do a mixdown. + switch (audio->config.in.channel_layout & HB_INPUT_CH_LAYOUT_DISCRETE_NO_LFE_MASK) + { + // stereo input or something not handled below + default: + case HB_INPUT_CH_LAYOUT_STEREO: + // mono gets mixed up to stereo & more than stereo gets mixed down + if ( audio->config.out.mixdown > HB_AMIXDOWN_STEREO ) + { + audio->config.out.mixdown = HB_AMIXDOWN_STEREO; + } + break; - /* mono sources */ - case HB_INPUT_CH_LAYOUT_MONO: - /* regardless of what stereo mixdown we've requested, a mono source always get mixed down - to mono if we can, and mixed up to stereo if we can't */ - if (audio->config.out.mixdown == HB_AMIXDOWN_MONO && audioCodecsSupportMono == 1) { + // mono input + case HB_INPUT_CH_LAYOUT_MONO: + // everything else passes through audio->config.out.mixdown = HB_AMIXDOWN_MONO; - } else { - audio->config.out.mixdown = HB_AMIXDOWN_STEREO; - } - break; - - /* stereo input */ - case HB_INPUT_CH_LAYOUT_STEREO: - /* if we've requested a mono mixdown, and it is supported, then do the mix */ - /* use stereo if not supported */ - if (audio->config.out.mixdown == HB_AMIXDOWN_MONO && audioCodecsSupportMono == 0) { - audio->config.out.mixdown = HB_AMIXDOWN_STEREO; - /* otherwise, preserve stereo regardless of if we requested something higher */ - } else if (audio->config.out.mixdown > HB_AMIXDOWN_STEREO) { - audio->config.out.mixdown = HB_AMIXDOWN_STEREO; - } - break; - - /* dolby (DPL1 aka Dolby Surround = 4.0 matrix-encoded) input */ - /* the A52 flags don't allow for a way to distinguish between DPL1 and DPL2 on a DVD, - so we always assume a DPL1 source for A52_DOLBY */ - case HB_INPUT_CH_LAYOUT_DOLBY: - /* if we've requested a mono mixdown, and it is supported, then do the mix */ - /* preserve dolby if not supported */ - if (audio->config.out.mixdown == HB_AMIXDOWN_MONO && audioCodecsSupportMono == 0) { - audio->config.out.mixdown = HB_AMIXDOWN_DOLBY; - /* otherwise, preserve dolby even if we requested something higher */ - /* a stereo mixdown will still be honoured here */ - } else if (audio->config.out.mixdown > HB_AMIXDOWN_DOLBY) { - audio->config.out.mixdown = HB_AMIXDOWN_DOLBY; - } - break; - - /* 3F/2R input */ - case HB_INPUT_CH_LAYOUT_3F2R: - /* if we've requested a mono mixdown, and it is supported, then do the mix */ - /* use dpl2 if not supported */ - if (audio->config.out.mixdown == HB_AMIXDOWN_MONO && audioCodecsSupportMono == 0) { - audio->config.out.mixdown = HB_AMIXDOWN_DOLBYPLII; - } else { - /* check if we have 3F2R input and also have an LFE - i.e. we have a 5.1 source) */ - if (audio->config.in.channel_layout & HB_INPUT_CH_LAYOUT_HAS_LFE) { - /* we have a 5.1 source */ - /* if we requested 6ch, but our audio format doesn't support it, then mix to DPLII instead */ - if (audio->config.out.mixdown == HB_AMIXDOWN_6CH && audioCodecsSupport6Ch == 0) { - audio->config.out.mixdown = HB_AMIXDOWN_DOLBYPLII; - } - } else { - /* we have a 5.0 source, so we can't do 6ch conversion - default to DPL II instead */ - if (audio->config.out.mixdown > HB_AMIXDOWN_DOLBYPLII) { - audio->config.out.mixdown = HB_AMIXDOWN_DOLBYPLII; - } + break; + + // dolby (DPL1 aka Dolby Surround = 4.0 matrix-encoded) input + // the A52 flags don't allow for a way to distinguish between DPL1 and + // DPL2 on a DVD so we always assume a DPL1 source for A52_DOLBY. + case HB_INPUT_CH_LAYOUT_DOLBY: + if ( audio->config.out.mixdown > HB_AMIXDOWN_DOLBY ) + { + audio->config.out.mixdown = HB_AMIXDOWN_DOLBY; } - } - /* all other mixdowns will have been preserved here */ - break; + break; - /* 3F/1R input */ - case HB_INPUT_CH_LAYOUT_3F1R: - /* if we've requested a mono mixdown, and it is supported, then do the mix */ - /* use dpl1 if not supported */ - if (audio->config.out.mixdown == HB_AMIXDOWN_MONO && audioCodecsSupportMono == 0) { - audio->config.out.mixdown = HB_AMIXDOWN_DOLBY; - } else { - /* we have a 4.0 or 4.1 source, so we can't do DPLII or 6ch conversion - default to DPL I instead */ - if (audio->config.out.mixdown > HB_AMIXDOWN_DOLBY) { + // 4 channel discrete + case HB_INPUT_CH_LAYOUT_2F2R: + case HB_INPUT_CH_LAYOUT_3F1R: + if ( audio->config.out.mixdown > HB_AMIXDOWN_DOLBY ) + { audio->config.out.mixdown = HB_AMIXDOWN_DOLBY; } - } - /* all other mixdowns will have been preserved here */ - break; + break; + + // 5 or 6 channel discrete + case HB_INPUT_CH_LAYOUT_3F2R: + if ( ! ( audio->config.in.channel_layout & + HB_INPUT_CH_LAYOUT_HAS_LFE ) ) + { + // we don't do 5 channel discrete so mixdown to DPLII + audio->config.out.mixdown = HB_AMIXDOWN_DOLBYPLII; + } + break; + } - default: - /* if we've requested a mono mixdown, and it is supported, then do the mix */ - if (audio->config.out.mixdown == HB_AMIXDOWN_MONO && audioCodecsSupportMono == 1) { - audio->config.out.mixdown = HB_AMIXDOWN_MONO; - /* mix everything else down to stereo */ - } else { - audio->config.out.mixdown = HB_AMIXDOWN_STEREO; + /* log the output mixdown */ + for (j = 0; j < hb_audio_mixdowns_count; j++) { + if (hb_audio_mixdowns[j].amixdown == audio->config.out.mixdown) { + if ( audio->config.out.mixdown != requested_mixdown ) + { + hb_log("work: sanitizing track %i mixdown %s to %s", i, hb_audio_mixdowns[requested_mixdown_index].human_readable_name, hb_audio_mixdowns[j].human_readable_name); + } + break; } - + } } - /* log the output mixdown */ - for (j = 0; j < hb_audio_mixdowns_count; j++) { - if (hb_audio_mixdowns[j].amixdown == audio->config.out.mixdown) { - hb_log( " + Actual mixdown: %s (%s)", hb_audio_mixdowns[j].human_readable_name, hb_audio_mixdowns[j].internal_name ); - break; - } - } - if (audio->config.out.codec == HB_ACODEC_VORBIS) audio->priv.config.vorbis.language = audio->config.lang.simple; /* set up the audio work structures */ - audio->priv.fifo_in = hb_fifo_init( 256 ); - audio->priv.fifo_raw = hb_fifo_init( FIFO_CPU_MULT * cpu_count ); - audio->priv.fifo_sync = hb_fifo_init( 256 ); - audio->priv.fifo_out = hb_fifo_init( FIFO_CPU_MULT * cpu_count ); + audio->priv.fifo_in = hb_fifo_init( FIFO_LARGE, FIFO_LARGE_WAKE ); + audio->priv.fifo_raw = hb_fifo_init( FIFO_SMALL, FIFO_SMALL_WAKE ); + audio->priv.fifo_sync = hb_fifo_init( FIFO_SMALL, FIFO_SMALL_WAKE ); + audio->priv.fifo_out = hb_fifo_init( FIFO_LARGE, FIFO_LARGE_WAKE ); + } + } + /* Synchronization */ + sync = hb_sync_init( job ); - /* - * Audio Decoder Thread - */ - switch( audio->config.in.codec ) + /* Video decoder */ + int vcodec = title->video_codec? title->video_codec : WORK_DECMPEG2; + hb_list_add( job->list_work, ( w = hb_get_work( vcodec ) ) ); + w->codec_param = title->video_codec_param; + w->fifo_in = job->fifo_mpeg2; + w->fifo_out = job->fifo_raw; + + /* Video renderer */ + hb_list_add( job->list_work, ( w = hb_get_work( WORK_RENDER ) ) ); + w->fifo_in = job->fifo_sync; + if( !job->indepth_scan ) + w->fifo_out = job->fifo_render; + else + w->fifo_out = NULL; + + if( !job->indepth_scan ) + { + + /* Video encoder */ + switch( job->vcodec ) { - case HB_ACODEC_AC3: - w = getWork( WORK_DECA52 ); - break; - case HB_ACODEC_DCA: - w = getWork( WORK_DECDCA ); - break; - case HB_ACODEC_MPGA: - w = getWork( WORK_DECAVCODEC ); - break; - case HB_ACODEC_LPCM: - w = getWork( WORK_DECLPCM ); - break; - default: - /* Invalid input codec */ - hb_error("Invalid input codec: %d", audio->config.in.codec); - *job->die = 1; - goto cleanup; + case HB_VCODEC_FFMPEG: + w = hb_get_work( WORK_ENCAVCODEC ); + break; + case HB_VCODEC_X264: + w = hb_get_work( WORK_ENCX264 ); + break; + case HB_VCODEC_THEORA: + w = hb_get_work( WORK_ENCTHEORA ); + break; } - w->fifo_in = audio->priv.fifo_in; - w->fifo_out = audio->priv.fifo_raw; - w->config = &audio->priv.config; - w->audio = audio; + w->fifo_in = job->fifo_render; + w->fifo_out = job->fifo_mpeg4; + w->config = &job->config; - /* FIXME: This feels really hackish, anything better? */ - audio_w = calloc( sizeof( hb_work_object_t ), 1 ); - audio_w = memcpy( audio_w, w, sizeof( hb_work_object_t )); - - hb_list_add( job->list_work, audio_w ); + hb_list_add( job->list_work, w ); + } + /* + * Look for the scanned subtitle in the existing subtitle list + */ + if ( !job->indepth_scan && interjob->select_subtitle && + ( job->pass == 0 || job->pass == 2 ) ) + { /* - * Audio Encoder Thread + * Disable forced subtitles if we didn't find any in the scan + * so that we display normal subtitles instead. + * + * select_subtitle implies that we did a scan. */ - switch( audio->config.out.codec ) + if( interjob->select_subtitle->config.force && + interjob->select_subtitle->forced_hits == 0 ) { - case HB_ACODEC_FAAC: - w = getWork( WORK_ENCFAAC ); - break; - case HB_ACODEC_LAME: - w = getWork( WORK_ENCLAME ); - break; - case HB_ACODEC_VORBIS: - w = getWork( WORK_ENCVORBIS ); - break; - case HB_ACODEC_AC3: - break; - case HB_ACODEC_DCA: /* These are all invalid output codecs. */ - default: - hb_error("Invalid audio codec: %#x", audio->config.out.codec); - w = NULL; - *job->die = 1; - goto cleanup; + interjob->select_subtitle->config.force = 0; + } + for( i=0; i < hb_list_count(title->list_subtitle); i++ ) + { + subtitle = hb_list_item( title->list_subtitle, i ); + + if( subtitle ) + { + /* + * Disable forced subtitles if we didn't find any in the scan + * so that we display normal subtitles instead. + * + * select_subtitle implies that we did a scan. + */ + if( interjob->select_subtitle->id == subtitle->id ) + { + *subtitle = *(interjob->select_subtitle); + free( interjob->select_subtitle ); + interjob->select_subtitle = NULL; + break; + } + } } - if( audio->config.out.codec != HB_ACODEC_AC3 ) + if( interjob->select_subtitle ) { /* - * Add the encoder thread if not doing AC-3 pass through + * Its not in the existing list + * + * Must be second pass of a two pass with subtitle scan enabled, so + * add the subtitle that we found on the first pass for use in this + * pass. */ - w->fifo_in = audio->priv.fifo_sync; - w->fifo_out = audio->priv.fifo_out; - w->config = &audio->priv.config; - w->audio = audio; + hb_list_add( title->list_subtitle, interjob->select_subtitle ); + interjob->select_subtitle = NULL; + } + } + + + for( i=0; i < hb_list_count(title->list_subtitle); i++ ) + { + subtitle = hb_list_item( title->list_subtitle, i ); - /* FIXME: This feels really hackish, anything better? */ - audio_w = calloc( sizeof( hb_work_object_t ), 1 ); - audio_w = memcpy( audio_w, w, sizeof( hb_work_object_t )); + if( subtitle ) + { + subtitle->fifo_in = hb_fifo_init( FIFO_SMALL, FIFO_SMALL_WAKE ); + subtitle->fifo_raw = hb_fifo_init( FIFO_SMALL, FIFO_SMALL_WAKE ); + subtitle->fifo_sync = hb_fifo_init( FIFO_SMALL, FIFO_SMALL_WAKE ); + subtitle->fifo_out = hb_fifo_init( FIFO_SMALL, FIFO_SMALL_WAKE ); + + if( (!job->indepth_scan || job->select_subtitle_config.force) && + subtitle->source == VOBSUB ) { + /* + * Don't add threads for subtitles when we are scanning, unless + * looking for forced subtitles. + */ + w = hb_get_work( WORK_DECVOBSUB ); + w->fifo_in = subtitle->fifo_in; + w->fifo_out = subtitle->fifo_raw; + w->subtitle = subtitle; + hb_list_add( job->list_work, w ); + } + + if( !job->indepth_scan && subtitle->source == CC608SUB ) + { + w = hb_get_work( WORK_DECCC608 ); + w->fifo_in = subtitle->fifo_in; + w->fifo_out = subtitle->fifo_raw; + hb_list_add( job->list_work, w ); + } + + if( !job->indepth_scan && subtitle->source == SRTSUB ) + { + w = hb_get_work( WORK_DECSRTSUB ); + w->fifo_in = subtitle->fifo_in; + w->fifo_out = subtitle->fifo_raw; + w->subtitle = subtitle; + hb_list_add( job->list_work, w ); + } + + if( !job->indepth_scan && subtitle->source == UTF8SUB ) + { + w = hb_get_work( WORK_DECUTF8SUB ); + w->fifo_in = subtitle->fifo_in; + w->fifo_out = subtitle->fifo_raw; + hb_list_add( job->list_work, w ); + } + + if( !job->indepth_scan && subtitle->source == TX3GSUB ) + { + w = hb_get_work( WORK_DECTX3GSUB ); + w->fifo_in = subtitle->fifo_in; + w->fifo_out = subtitle->fifo_raw; + hb_list_add( job->list_work, w ); + } + + if( !job->indepth_scan && subtitle->source == SSASUB ) + { + w = hb_get_work( WORK_DECSSASUB ); + w->fifo_in = subtitle->fifo_in; + w->fifo_out = subtitle->fifo_raw; + hb_list_add( job->list_work, w ); + } - hb_list_add( job->list_work, audio_w ); + if( !job->indepth_scan && + subtitle->format == PICTURESUB + && subtitle->config.dest == PASSTHRUSUB ) + { + /* + * Passing through a subtitle picture, this will have to + * be rle encoded before muxing. + */ + w = hb_get_work( WORK_ENCVOBSUB ); + w->fifo_in = subtitle->fifo_sync; + w->fifo_out = subtitle->fifo_out; + w->subtitle = subtitle; + hb_list_add( job->list_work, w ); + } } + } + + if( !job->indepth_scan ) + { + for( i = 0; i < hb_list_count( title->list_audio ); i++ ) + { + audio = hb_list_item( title->list_audio, i ); + + /* + * Audio Decoder Thread + */ + if ( ( w = hb_codec_decoder( audio->config.in.codec ) ) == NULL ) + { + hb_error("Invalid input codec: %d", audio->config.in.codec); + *job->die = 1; + goto cleanup; + } + w->fifo_in = audio->priv.fifo_in; + w->fifo_out = audio->priv.fifo_raw; + w->config = &audio->priv.config; + w->audio = audio; + w->codec_param = audio->config.in.codec_param; + + hb_list_add( job->list_work, w ); + /* + * Audio Encoder Thread + */ + if( audio->config.out.codec != HB_ACODEC_AC3 && + audio->config.out.codec != HB_ACODEC_DCA ) + { + /* + * Add the encoder thread if not doing AC-3 pass through + */ + if ( ( w = hb_codec_encoder( audio->config.out.codec ) ) == NULL ) + { + hb_error("Invalid audio codec: %#x", audio->config.out.codec); + w = NULL; + *job->die = 1; + goto cleanup; + } + w->fifo_in = audio->priv.fifo_sync; + w->fifo_out = audio->priv.fifo_out; + w->config = &audio->priv.config; + w->audio = audio; + hb_list_add( job->list_work, w ); + } + } + } + + if( job->chapter_markers && job->chapter_start == job->chapter_end ) + { + job->chapter_markers = 0; + hb_log("work: only 1 chapter, disabling chapter markers"); } + /* Display settings */ + hb_display_job_info( job ); + /* Init read & write threads */ job->reader = hb_reader_init( job ); - hb_log( " + output: %s", job->file ); - job->muxer = hb_muxer_init( job ); job->done = 0; /* Launch processing threads */ - for( i = 1; i < hb_list_count( job->list_work ); i++ ) + for( i = 0; i < hb_list_count( job->list_work ); i++ ) { w = hb_list_item( job->list_work, i ); w->done = &job->done; w->thread_sleep_interval = 10; - w->init( w, job ); + if( w->init( w, job ) ) + { + hb_error( "Failure to initialise thread '%s'", w->name ); + *job->die = 1; + goto cleanup; + } w->thread = hb_thread_init( w->name, work_loop, w, HB_LOW_PRIORITY ); } - done = 0; - w = hb_list_item( job->list_work, 0 ); - w->thread_sleep_interval = 50; - w->init( w, job ); - while( !*job->die ) + if ( job->indepth_scan ) + { + muxer = NULL; + w = sync; + sync->done = &job->done; + } + else { - if( ( w->status = w->work( w, NULL, NULL ) ) == HB_WORK_DONE ) + sync->done = &job->done; + sync->thread_sleep_interval = 10; + if( sync->init( w, job ) ) { - done = 1; + hb_error( "Failure to initialise thread '%s'", w->name ); + *job->die = 1; + goto cleanup; } - if( done && - final_w->status == HB_WORK_DONE && - !hb_fifo_size( job->fifo_mpeg4 ) ) + sync->thread = hb_thread_init( sync->name, work_loop, sync, + HB_LOW_PRIORITY ); + + // The muxer requires track information that's set up by the encoder + // init routines so we have to init the muxer last. + muxer = hb_muxer_init( job ); + w = muxer; + } + + while ( !*job->die && !*w->done && w->status != HB_WORK_DONE ) + { + hb_buffer_t * buf_in, * buf_out; + + buf_in = hb_fifo_get_wait( w->fifo_in ); + if ( buf_in == NULL ) + continue; + if ( *job->die ) { + if( buf_in ) + { + hb_buffer_close( &buf_in ); + } break; } - hb_snooze( w->thread_sleep_interval ); + + buf_out = NULL; + w->status = w->work( w, &buf_in, &buf_out ); + + if( buf_in ) + { + hb_buffer_close( &buf_in ); + } + if ( buf_out && w->fifo_out == NULL ) + { + hb_buffer_close( &buf_out ); + } + if( buf_out ) + { + while ( !*job->die ) + { + if ( hb_fifo_full_wait( w->fifo_out ) ) + { + hb_fifo_push( w->fifo_out, buf_out ); + break; + } + } + } } - hb_list_rem( job->list_work, w ); - w->close( w ); + + hb_handle_t * h = job->h; + hb_state_t state; + hb_get_state( h, &state ); + + hb_log("work: average encoding speed for job is %f fps", state.param.working.rate_avg); + job->done = 1; + if( muxer != NULL ) + { + muxer->close( muxer ); + free( muxer ); + + if( sync->thread != NULL ) + { + hb_thread_close( &sync->thread ); + sync->close( sync ); + } + free( sync ); + } cleanup: + /* Stop the write thread (thread_close will block until the muxer finishes) */ + job->done = 1; + /* Close work objects */ while( ( w = hb_list_item( job->list_work, 0 ) ) ) { hb_list_rem( job->list_work, w ); - if( w != NULL && w->thread != NULL ) + if( w->thread != NULL ) { hb_thread_close( &w->thread ); w->close( w ); } - - /* FIXME: This feels really hackish, anything better? */ - if ( w->id == WORK_DECA52 || - w->id == WORK_DECDCA || - w->id == WORK_DECLPCM || - w->id == WORK_ENCFAAC || - w->id == WORK_ENCLAME || - w->id == WORK_ENCVORBIS ) - { - free( w ); - w = NULL; - } + free( w ); } hb_list_close( &job->list_work ); - /* Stop read & write threads */ + /* Stop the read thread */ if( job->reader != NULL ) hb_thread_close( &job->reader ); - if( job->muxer != NULL ) - hb_thread_close( &job->muxer ); /* Close fifos */ hb_fifo_close( &job->fifo_mpeg2 ); @@ -693,6 +1029,8 @@ cleanup: { hb_fifo_close( &subtitle->fifo_in ); hb_fifo_close( &subtitle->fifo_raw ); + hb_fifo_close( &subtitle->fifo_sync ); + hb_fifo_close( &subtitle->fifo_out ); } } for( i = 0; i < hb_list_count( title->list_audio ); i++ ) @@ -717,9 +1055,14 @@ cleanup: for( i=0; i < hb_list_count( title->list_subtitle ); i++ ) { subtitle = hb_list_item( title->list_subtitle, i ); + hb_log( "Subtitle stream 0x%x '%s': %d hits (%d forced)", subtitle->id, subtitle->lang, subtitle->hits, subtitle->forced_hits ); + + if( subtitle->hits == 0 ) + continue; + if( subtitle->hits > subtitle_highest ) { subtitle_highest = subtitle->hits; @@ -741,67 +1084,48 @@ cleanup: } } - if( job->native_language ) { + + if( subtitle_forced_id ) + { /* - * We still have a native_language, so the audio and subtitles are - * different, so in this case it is a foreign film and we want to - * select the subtitle with the highest hits in our language. + * If there are any subtitle streams with forced subtitles + * then select it in preference to the lowest. */ - subtitle_hit = subtitle_highest_id; - hb_log( "Found a native-language subtitle id 0x%x", subtitle_hit); - } else { - if( subtitle_forced_id ) - { - /* - * If there are any subtitle streams with forced subtitles - * then select it in preference to the lowest. - */ - subtitle_hit = subtitle_forced_id; - hb_log("Found a subtitle candidate id 0x%x (contains forced subs)", - subtitle_hit); - } else if( subtitle_lowest < subtitle_highest ) + subtitle_hit = subtitle_forced_id; + hb_log("Found a subtitle candidate id 0x%x (contains forced subs)", + subtitle_hit); + } else if( subtitle_lowest < subtitle_highest ) + { + /* + * OK we have more than one, and the lowest is lower, + * but how much lower to qualify for turning it on by + * default? + * + * Let's say 10% as a default. + */ + if( subtitle_lowest < ( subtitle_highest * 0.1 ) ) { - /* - * OK we have more than one, and the lowest is lower, - * but how much lower to qualify for turning it on by - * default? - * - * Let's say 10% as a default. - */ - if( subtitle_lowest < ( subtitle_highest * 0.1 ) ) - { - subtitle_hit = subtitle_lowest_id; - hb_log( "Found a subtitle candidate id 0x%x", - subtitle_hit ); - } else { - hb_log( "No candidate subtitle detected during subtitle-scan"); - } + subtitle_hit = subtitle_lowest_id; + hb_log( "Found a subtitle candidate id 0x%x", + subtitle_hit ); + } else { + hb_log( "No candidate subtitle detected during subtitle-scan"); } } } - if( job->select_subtitle ) + if( job->indepth_scan ) { - if( job->indepth_scan ) + for( i=0; i < hb_list_count( title->list_subtitle ); i++ ) { - for( i=0; i < hb_list_count( title->list_subtitle ); i++ ) + subtitle = hb_list_item( title->list_subtitle, i ); + if( subtitle->id == subtitle_hit ) { - subtitle = hb_list_item( title->list_subtitle, i ); - if( subtitle->id == subtitle_hit ) - { - hb_list_rem( title->list_subtitle, subtitle ); - *( job->select_subtitle ) = subtitle; - } + subtitle->config = job->select_subtitle_config; + hb_list_rem( title->list_subtitle, subtitle ); + interjob->select_subtitle = subtitle; + break; } - } else { - /* - * Must be the end of pass 0 or 2 - we don't need this anymore. - * - * Have to put the subtitle list back together in the title though - * or the GUI will have a hissy fit. - */ - free( job->select_subtitle ); - job->select_subtitle = NULL; } } @@ -822,7 +1146,7 @@ cleanup: } /** - * Performs the work objects specific work function. + * Performs the work object's specific work function. * Loops calling work function for associated work object. Sleeps when fifo is full. * Monitors work done indicator. * Exits loop when work indiactor is set. @@ -833,21 +1157,19 @@ static void work_loop( void * _w ) hb_work_object_t * w = _w; hb_buffer_t * buf_in, * buf_out; - while( !*w->done ) + while( !*w->done && w->status != HB_WORK_DONE ) { -#if 0 - hb_lock( job->pause ); - hb_unlock( job->pause ); -#endif - if( hb_fifo_is_full( w->fifo_out ) || -// if( (hb_fifo_percent_full( w->fifo_out ) > 0.8) || - !( buf_in = hb_fifo_get( w->fifo_in ) ) ) - { - hb_snooze( w->thread_sleep_interval ); -// w->thread_sleep_interval += 1; + buf_in = hb_fifo_get_wait( w->fifo_in ); + if ( buf_in == NULL ) continue; + if ( *w->done ) + { + if( buf_in ) + { + hb_buffer_close( &buf_in ); + } + break; } -// w->thread_sleep_interval = MAX(1, (w->thread_sleep_interval - 1)); w->status = w->work( w, &buf_in, &buf_out ); @@ -859,7 +1181,7 @@ static void work_loop( void * _w ) if( buf_in && buf_out && buf_in->new_chap && buf_in->start == buf_out->start) { // restore log below to debug chapter mark propagation problems - //hb_log("work %s: Copying Chapter Break @ %lld", w->name, buf_in->start); + //hb_log("work %s: Copying Chapter Break @ %"PRId64, w->name, buf_in->start); buf_out->new_chap = buf_in->new_chap; } @@ -867,9 +1189,28 @@ static void work_loop( void * _w ) { hb_buffer_close( &buf_in ); } + if ( buf_out && w->fifo_out == NULL ) + { + hb_buffer_close( &buf_out ); + } if( buf_out ) { - hb_fifo_push( w->fifo_out, buf_out ); + while ( !*w->done ) + { + if ( hb_fifo_full_wait( w->fifo_out ) ) + { + hb_fifo_push( w->fifo_out, buf_out ); + break; + } + } } } + // Consume data in incoming fifo till job complete so that + // residual data does not stall the pipeline + while( !*w->done ) + { + buf_in = hb_fifo_get_wait( w->fifo_in ); + if ( buf_in != NULL ) + hb_buffer_close( &buf_in ); + } }