diff options
Diffstat (limited to 'libhb/work.c')
-rw-r--r-- | libhb/work.c | 351 |
1 files changed, 167 insertions, 184 deletions
diff --git a/libhb/work.c b/libhb/work.c index 9c2ecbf9a..cf801e48b 100644 --- a/libhb/work.c +++ b/libhb/work.c @@ -70,19 +70,45 @@ 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 ); + } + return NULL; +} + /** * Job initialization rountine. * Initializes fifos. @@ -99,10 +125,6 @@ static void do_job( hb_job_t * job, int cpu_count ) 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_audio_t * audio; @@ -250,17 +272,19 @@ static void do_job( hb_job_t * job, int cpu_count ) job->fifo_mpeg4 = hb_fifo_init( FIFO_CPU_MULT * cpu_count ); /* Synchronization */ - hb_list_add( job->list_work, ( w = getWork( WORK_SYNC ) ) ); + hb_list_add( job->list_work, ( w = hb_get_work( WORK_SYNC ) ) ); w->fifo_in = NULL; w->fifo_out = NULL; /* Video decoder */ - hb_list_add( job->list_work, ( w = getWork( WORK_DECMPEG2 ) ) ); + 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 = getWork( WORK_RENDER ) ) ); + hb_list_add( job->list_work, ( w = hb_get_work( WORK_RENDER ) ) ); w->fifo_in = job->fifo_sync; w->fifo_out = job->fifo_render; if ( job->indepth_scan ) @@ -278,21 +302,21 @@ static void do_job( hb_job_t * job, int cpu_count ) { case HB_VCODEC_FFMPEG: hb_log( " + encoder FFmpeg" ); - w = getWork( WORK_ENCAVCODEC ); + w = hb_get_work( WORK_ENCAVCODEC ); break; case HB_VCODEC_XVID: hb_log( " + encoder XviD" ); - w = getWork( WORK_ENCXVID ); + w = hb_get_work( 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 ); + w = hb_get_work( WORK_ENCX264 ); break; case HB_VCODEC_THEORA: hb_log( " + encoder Theora" ); - w = getWork( WORK_ENCTHEORA ); + w = hb_get_work( WORK_ENCTHEORA ); break; } w->fifo_in = job->fifo_render; @@ -354,20 +378,10 @@ static void do_job( hb_job_t * job, int cpu_count ) * Don't add threads for subtitles when we are scanning, unless * looking for forced subtitles. */ - if( sub_w != NULL ) - { - /* - * 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 ); - } - hb_list_add( job->list_work, sub_w ); - sub_w->fifo_in = subtitle->fifo_in; - sub_w->fifo_out = subtitle->fifo_raw; + w = hb_get_work( WORK_DECSUB ); + w->fifo_in = subtitle->fifo_in; + w->fifo_out = subtitle->fifo_raw; + hb_list_add( job->list_work, w ); } } } @@ -427,113 +441,129 @@ static void do_job( hb_job_t * job, int cpu_count ) /* sense-check the requested mixdown */ - if( audio->config.out.mixdown == 0 && audio->config.out.codec != HB_ACODEC_AC3 ) + if( audio->config.out.mixdown == 0 && + audio->config.out.codec != HB_ACODEC_AC3 ) + { + /* + * Mixdown wasn't specified and this is not pass-through, + * set a default mixdown of stereo. + */ + audio->config.out.mixdown = HB_AMIXDOWN_STEREO; + } + + // 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. +#define CAN_MIXDOWN(a) ( a->config.in.codec & (HB_ACODEC_AC3|HB_ACODEC_DCA) ) +#define STEREO_ONLY(a) ( a->config.out.codec & HB_ACODEC_LAME ) + + 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 ( STEREO_ONLY( audio ) || + audio->config.out.mixdown > HB_AMIXDOWN_STEREO) { - /* - * Mixdown wasn't specified and this is not pass-through, set a default mixdown - * of stereo. - */ audio->config.out.mixdown = HB_AMIXDOWN_STEREO; } + break; - /* 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)); - - /* find out what the format of our source audio is */ - switch (audio->config.in.channel_layout & HB_INPUT_CH_LAYOUT_DISCRETE_NO_LFE_MASK) { - - /* mono sources */ + // mono input 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) { - audio->config.out.mixdown = HB_AMIXDOWN_MONO; - } else { + if ( STEREO_ONLY( audio ) ) + { + if ( !CAN_MIXDOWN( audio ) ) + { + // XXX we're hosed - we can't mix up & lame can't handle + // the input format. The user shouldn't be able to make + // this choice. It's too late to do anything about it now + // so complain in the log & let things abort in lame. + hb_log( "ERROR - can't use lame mp3 audio output with " + "mono audio stream %x - output will be messed up", + audio->id ); + } 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; + else + { + // everything else passes through + audio->config.out.mixdown = HB_AMIXDOWN_MONO; } 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 */ + // 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) { + if ( STEREO_ONLY( audio ) || !CAN_MIXDOWN( audio ) || + audio->config.out.mixdown > HB_AMIXDOWN_DOLBY ) + { audio->config.out.mixdown = HB_AMIXDOWN_DOLBY; } break; - /* 3F/2R input */ + // 4 channel discrete + case HB_INPUT_CH_LAYOUT_2F2R: + case HB_INPUT_CH_LAYOUT_3F1R: + if ( CAN_MIXDOWN( audio ) ) + { + if ( STEREO_ONLY( audio ) || + audio->config.out.mixdown > HB_AMIXDOWN_DOLBY ) + { + audio->config.out.mixdown = HB_AMIXDOWN_DOLBY; + } + } + else + { + // XXX we can't mixdown & don't have any way to specify + // 4 channel discrete output so we're hosed. + hb_log( "ERROR - can't handle 4 channel discrete audio stream " + "%x - output will be messed up", audio->id ); + } + break; + + // 5 or 6 channel discrete 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; + if ( CAN_MIXDOWN( audio ) ) + { + if ( STEREO_ONLY( audio ) ) + { + if ( audio->config.out.mixdown < HB_AMIXDOWN_STEREO ) + { + audio->config.out.mixdown = HB_AMIXDOWN_STEREO; } - } 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) { + else if ( audio->config.out.mixdown > HB_AMIXDOWN_DOLBYPLII ) + { audio->config.out.mixdown = HB_AMIXDOWN_DOLBYPLII; } } - } - /* all other mixdowns will have been preserved here */ - 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) { - audio->config.out.mixdown = HB_AMIXDOWN_DOLBY; + else 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; } } - /* all other mixdowns will have been preserved here */ - 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; + else if ( ! ( audio->config.in.channel_layout & + HB_INPUT_CH_LAYOUT_HAS_LFE ) ) + { + // XXX we can't mixdown & don't have any way to specify + // 5 channel discrete output so we're hosed. + hb_log( "ERROR - can't handle 5 channel discrete audio stream " + "%x - output will be messed up", audio->id ); } - + else + { + // we can't mixdown so force 6 channel discrete + audio->config.out.mixdown = HB_AMIXDOWN_6CH; + } + break; } /* log the output mixdown */ @@ -557,79 +587,42 @@ static void do_job( hb_job_t * job, int cpu_count ) /* * Audio Decoder Thread */ - switch( audio->config.in.codec ) + if ( ( w = hb_codec_decoder( audio->config.in.codec ) ) == NULL ) { - 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; + 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->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; - /* 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 ); /* * Audio Encoder Thread */ - switch( audio->config.out.codec ) - { - 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; - } - if( audio->config.out.codec != HB_ACODEC_AC3 ) { /* * Add the encoder thread if not doing AC-3 pass through */ - w->fifo_in = audio->priv.fifo_sync; - w->fifo_out = audio->priv.fifo_out; - w->config = &audio->priv.config; - w->audio = audio; - - /* 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 ( ( 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, audio_w ); + hb_list_add( job->list_work, w ); } - - } /* Init read & write threads */ @@ -671,6 +664,7 @@ static void do_job( hb_job_t * job, int cpu_count ) } hb_list_rem( job->list_work, w ); w->close( w ); + free( w ); job->done = 1; cleanup: @@ -678,23 +672,12 @@ cleanup: 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 ); @@ -847,7 +830,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. |