From cc8b3643ceb7da7bb82af0722aa33c5f7d2c02a2 Mon Sep 17 00:00:00 2001 From: jstebbins Date: Mon, 17 Jan 2011 18:57:16 +0000 Subject: fix ffmpeg multiple audio decode issue we can now have one ffmpeg audio input track fan out to multiple output tracks. git-svn-id: svn://svn.handbrake.fr/HandBrake/trunk@3753 b64f7644-9d1e-0410-96f1-a4d463321fa5 --- libhb/common.c | 4 + libhb/common.h | 2 + libhb/decavcodec.c | 262 +++++++++++++++++++++++++++++++++++++++++++++-------- libhb/stream.c | 2 +- libhb/work.c | 86 ++++++++++++------ 5 files changed, 289 insertions(+), 67 deletions(-) diff --git a/libhb/common.c b/libhb/common.c index f1926e9dd..6b1d830a3 100644 --- a/libhb/common.c +++ b/libhb/common.c @@ -1051,6 +1051,10 @@ void hb_title_close( hb_title_t ** _t ) while( ( audio = hb_list_item( t->list_audio, 0 ) ) ) { + if ( audio->priv.ff_audio_list != NULL ) + { + hb_list_close( &audio->priv.ff_audio_list ); + } hb_list_rem( t->list_audio, audio ); free( audio ); } diff --git a/libhb/common.h b/libhb/common.h index 0dd3a2d42..bd1eaddc8 100644 --- a/libhb/common.h +++ b/libhb/common.h @@ -447,6 +447,8 @@ struct hb_audio_s hb_esconfig_t config; hb_mux_data_t * mux_data; hb_fifo_t * scan_cache; + + hb_list_t * ff_audio_list; } priv; }; #endif diff --git a/libhb/decavcodec.c b/libhb/decavcodec.c index ecb1d3cb4..609c07c52 100644 --- a/libhb/decavcodec.c +++ b/libhb/decavcodec.c @@ -96,6 +96,7 @@ struct hb_work_private_s AVCodecContext *context; AVCodecParserContext *parser; hb_list_t *list; + hb_list_t *ff_audio_list; double duration; // frame duration (for video) double pts_next; // next pts we expect to generate int64_t pts; // (video) pts passing from parser to decoder @@ -187,6 +188,7 @@ static void heap_push( pts_heap_t *heap, int64_t v ) static int decavcodecInit( hb_work_object_t * w, hb_job_t * job ) { AVCodec * codec; + int i; hb_work_private_t * pv = calloc( 1, sizeof( hb_work_private_t ) ); w->private_data = pv; @@ -224,6 +226,36 @@ static int decavcodecInit( hb_work_object_t * w, hb_job_t * job ) w->audio->config.out.mixdown); hb_downmix_set_chan_map( pv->downmix, &hb_smpte_chan_map, pv->out_map ); } + + pv->ff_audio_list = hb_list_init(); + for ( i = 0; i < hb_list_count( w->audio->priv.ff_audio_list ); i++ ) + { + hb_work_private_t * ff_pv = calloc( 1, sizeof( hb_work_private_t ) ); + hb_list_add( pv->ff_audio_list, ff_pv ); + + hb_audio_t *audio = hb_list_item( w->audio->priv.ff_audio_list, i ); + + ff_pv->list = hb_list_init(); + ff_pv->job = job; + + if ( audio->config.out.codec == HB_ACODEC_AC3 ) + { + // ffmpegs audio encoder expect an smpte chan map as input. + // So we need to map the decoders output to smpte. + ff_pv->out_map = &hb_smpte_chan_map; + } + else + { + ff_pv->out_map = &hb_qt_chan_map; + } + if ( hb_need_downmix( audio->config.in.channel_layout, + audio->config.out.mixdown) ) + { + ff_pv->downmix = hb_downmix_init(audio->config.in.channel_layout, + audio->config.out.mixdown); + hb_downmix_set_chan_map( ff_pv->downmix, &hb_smpte_chan_map, ff_pv->out_map ); + } + } } return 0; @@ -234,9 +266,9 @@ static int decavcodecInit( hb_work_object_t * w, hb_job_t * job ) *********************************************************************** * **********************************************************************/ -static void decavcodecClose( hb_work_object_t * w ) +static void closePrivData( hb_work_private_t ** ppv ) { - hb_work_private_t * pv = w->private_data; + hb_work_private_t * pv = *ppv; if ( pv ) { @@ -277,10 +309,102 @@ static void decavcodecClose( hb_work_object_t * w ) pv->downmix_buffer = NULL; } free( pv ); + } + *ppv = NULL; +} + +static void decavcodecClose( hb_work_object_t * w ) +{ + hb_work_private_t * pv = w->private_data; + + if ( pv ) + { + if ( pv->ff_audio_list != NULL ) + { + hb_work_private_t * ff_pv; + while ( ( ff_pv = hb_list_item( pv->list, 0 ) ) != NULL ) + { + hb_list_rem( pv->ff_audio_list, ff_pv ); + closePrivData( &ff_pv ); + } + } + closePrivData( &pv ); w->private_data = NULL; } } +static void writeAudioEof( hb_work_object_t * w ) +{ + hb_work_private_t * pv = w->private_data; + hb_audio_t * audio = w->audio; + int i; + hb_buffer_t * buf; + + for ( i = 0; i < hb_list_count( audio->priv.ff_audio_list ); i++ ) + { + hb_audio_t *ff_audio = hb_list_item( audio->priv.ff_audio_list, i ); + hb_work_private_t *ff_pv = hb_list_item( pv->ff_audio_list, i ); + if ( ff_pv ) + { + buf = hb_buffer_init( 0 ); + if ( buf ) + { + while ( !*w->done ) + { + if ( hb_fifo_full_wait( ff_audio->priv.fifo_raw ) ) + { + hb_fifo_push( ff_audio->priv.fifo_raw, buf ); + buf = NULL; + break; + } + } + if ( buf ) + { + // w->done == true while waiting + hb_buffer_close( &buf ); + break; + } + } + } + } +} + +static void writeAudioFifos( hb_work_object_t * w ) +{ + hb_work_private_t * pv = w->private_data; + hb_audio_t * audio = w->audio; + int i; + hb_buffer_t * buf; + + for ( i = 0; i < hb_list_count( audio->priv.ff_audio_list ); i++ ) + { + hb_audio_t *ff_audio = hb_list_item( audio->priv.ff_audio_list, i ); + hb_work_private_t *ff_pv = hb_list_item( pv->ff_audio_list, i ); + if ( ff_pv ) + { + buf = link_buf_list( ff_pv ); + if ( buf ) + { + while ( !*w->done ) + { + if ( hb_fifo_full_wait( ff_audio->priv.fifo_raw ) ) + { + hb_fifo_push( ff_audio->priv.fifo_raw, buf ); + buf = NULL; + break; + } + } + if ( buf ) + { + // w->done == true while waiting + hb_buffer_close( &buf ); + break; + } + } + } + } +} + /*********************************************************************** * Work *********************************************************************** @@ -297,6 +421,7 @@ static int decavcodecWork( hb_work_object_t * w, hb_buffer_t ** buf_in, /* EOF on input stream - send it downstream & say that we're done */ *buf_out = in; *buf_in = NULL; + writeAudioEof( w ); return HB_WORK_DONE; } @@ -349,6 +474,7 @@ static int decavcodecWork( hb_work_object_t * w, hb_buffer_t ** buf_in, decodeAudio( w->audio, pv, parser_output_buffer, parser_output_buffer_len ); } } + writeAudioFifos( w ); *buf_out = link_buf_list( pv ); return HB_WORK_OK; } @@ -1241,6 +1367,7 @@ static int decavcodecviInit( hb_work_object_t * w, hb_job_t * job ) { hb_work_private_t *pv = calloc( 1, sizeof( hb_work_private_t ) ); + int i; w->private_data = pv; pv->job = job; pv->list = hb_list_init(); @@ -1266,6 +1393,36 @@ static int decavcodecviInit( hb_work_object_t * w, hb_job_t * job ) w->audio->config.out.mixdown); hb_downmix_set_chan_map( pv->downmix, &hb_smpte_chan_map, pv->out_map ); } + + pv->ff_audio_list = hb_list_init(); + for ( i = 0; i < hb_list_count( w->audio->priv.ff_audio_list ); i++ ) + { + hb_work_private_t * ff_pv = calloc( 1, sizeof( hb_work_private_t ) ); + hb_list_add( pv->ff_audio_list, ff_pv ); + + hb_audio_t *audio = hb_list_item( w->audio->priv.ff_audio_list, i ); + + ff_pv->list = hb_list_init(); + ff_pv->job = job; + + if ( audio->config.out.codec == HB_ACODEC_AC3 ) + { + // ffmpegs audio encoder expect an smpte chan map as input. + // So we need to map the decoders output to smpte. + ff_pv->out_map = &hb_smpte_chan_map; + } + else + { + ff_pv->out_map = &hb_qt_chan_map; + } + if ( hb_need_downmix( audio->config.in.channel_layout, + audio->config.out.mixdown) ) + { + ff_pv->downmix = hb_downmix_init(audio->config.in.channel_layout, + audio->config.out.mixdown); + hb_downmix_set_chan_map( ff_pv->downmix, &hb_smpte_chan_map, ff_pv->out_map ); + } + } } return 0; @@ -1337,6 +1494,50 @@ static int decavcodecviInfo( hb_work_object_t *w, hb_work_info_t *info ) return 0; } +static hb_buffer_t * downmixAudio( + hb_audio_t *audio, + hb_work_private_t *pv, + int16_t *buffer, + int channels, + int nsamples ) +{ + hb_buffer_t * buf = NULL; + + if ( pv->downmix ) + { + pv->downmix_buffer = realloc(pv->downmix_buffer, nsamples * sizeof(hb_sample_t)); + + int i; + for( i = 0; i < nsamples; ++i ) + { + pv->downmix_buffer[i] = buffer[i]; + } + + int n_ch_samples = nsamples / channels; + int out_channels = HB_AMIXDOWN_GET_DISCRETE_CHANNEL_COUNT(audio->config.out.mixdown); + + buf = hb_buffer_init( n_ch_samples * out_channels * sizeof(float) ); + hb_sample_t *samples = (hb_sample_t *)buf->data; + hb_downmix(pv->downmix, samples, pv->downmix_buffer, n_ch_samples); + } + else + { + buf = hb_buffer_init( nsamples * sizeof(float) ); + float *fl32 = (float *)buf->data; + int i; + for( i = 0; i < nsamples; ++i ) + { + fl32[i] = buffer[i]; + } + int n_ch_samples = nsamples / channels; + hb_layout_remap( &hb_smpte_chan_map, pv->out_map, + audio->config.in.channel_layout, + fl32, n_ch_samples ); + } + + return buf; +} + static void decodeAudio( hb_audio_t * audio, hb_work_private_t *pv, uint8_t *data, int size ) { AVCodecContext *context = pv->context; @@ -1410,46 +1611,33 @@ static void decodeAudio( hb_audio_t * audio, hb_work_private_t *pv, uint8_t *dat } hb_buffer_t * buf; - - if ( pv->downmix ) + double pts = pv->pts_next; + double pts_next = pts + nsamples * pv->duration; + buf = downmixAudio( audio, pv, buffer, context->channels, nsamples ); + if ( buf ) { - pv->downmix_buffer = realloc(pv->downmix_buffer, nsamples * sizeof(hb_sample_t)); - - int i; - for( i = 0; i < nsamples; ++i ) - { - pv->downmix_buffer[i] = buffer[i]; - } - - int n_ch_samples = nsamples / context->channels; - int channels = HB_AMIXDOWN_GET_DISCRETE_CHANNEL_COUNT(audio->config.out.mixdown); - - buf = hb_buffer_init( n_ch_samples * channels * sizeof(float) ); - hb_sample_t *samples = (hb_sample_t *)buf->data; - hb_downmix(pv->downmix, samples, pv->downmix_buffer, n_ch_samples); + buf->start = pts; + buf->stop = pts_next; + hb_list_add( pv->list, buf ); } - else + + int i; + for ( i = 0; i < hb_list_count( audio->priv.ff_audio_list ); i++ ) { - buf = hb_buffer_init( nsamples * sizeof(float) ); - float *fl32 = (float *)buf->data; - int i; - for( i = 0; i < nsamples; ++i ) + hb_audio_t *ff_audio = hb_list_item( audio->priv.ff_audio_list, i ); + hb_work_private_t *ff_pv = hb_list_item( pv->ff_audio_list, i ); + if ( ff_pv ) { - fl32[i] = buffer[i]; + buf = downmixAudio( ff_audio, ff_pv, buffer, context->channels, nsamples ); + if ( buf ) + { + buf->start = pts; + buf->stop = pts_next; + hb_list_add( ff_pv->list, buf ); + } } - int n_ch_samples = nsamples / context->channels; - hb_layout_remap( &hb_smpte_chan_map, pv->out_map, - audio->config.in.channel_layout, - fl32, n_ch_samples ); } - - double pts = pv->pts_next; - buf->start = pts; - pts += nsamples * pv->duration; - buf->stop = pts; - pv->pts_next = pts; - - hb_list_add( pv->list, buf ); + pv->pts_next = pts_next; // if we allocated a buffer for sample format conversion, free it if ( buffer != pv->buffer ) @@ -1468,6 +1656,7 @@ static int decavcodecaiWork( hb_work_object_t *w, hb_buffer_t **buf_in, /* EOF on input stream - send it downstream & say that we're done */ *buf_out = *buf_in; *buf_in = NULL; + writeAudioEof( w ); return HB_WORK_DONE; } @@ -1502,6 +1691,7 @@ static int decavcodecaiWork( hb_work_object_t *w, hb_buffer_t **buf_in, } prepare_ffmpeg_buffer( in ); decodeAudio( w->audio, pv, in->data, in->size ); + writeAudioFifos( w ); *buf_out = link_buf_list( pv ); return HB_WORK_OK; diff --git a/libhb/stream.c b/libhb/stream.c index 7d9b016cc..c90f9490a 100644 --- a/libhb/stream.c +++ b/libhb/stream.c @@ -1461,7 +1461,7 @@ int hb_stream_seek_chapter( hb_stream_t * stream, int chapter_num ) // that causes the problem. since hb_stream_seek_chapter // is called before we start reading, make sure // we do a seek here. - av_seek_frame( stream->ffmpeg_ic, -1, ffmpeg_initial_timestamp( stream ), AVSEEK_FLAG_BACKWARD ); + av_seek_frame( stream->ffmpeg_ic, -1, ffmpeg_initial_timestamp( stream ), AVSEEK_FLAG_BACKWARD | AVSEEK_FLAG_ANY ); } return 1; } diff --git a/libhb/work.c b/libhb/work.c index 53ab3fe9b..045f5b9bb 100644 --- a/libhb/work.c +++ b/libhb/work.c @@ -393,6 +393,27 @@ void correct_framerate( hb_job_t * job ) job->vrate = job->vrate_base * ( (double)real_frames * 90000 / interjob->total_time ); } +static int check_ff_audio( hb_list_t *list_audio, hb_audio_t *ff_audio ) +{ + int i; + + for( i = 0; i < hb_list_count( list_audio ); i++ ) + { + hb_audio_t * audio = hb_list_item( list_audio, i ); + + if ( audio == ff_audio ) + break; + + if ( audio->config.in.codec == HB_ACODEC_FFMPEG && + audio->id == ff_audio->id ) + { + hb_list_add( audio->priv.ff_audio_list, ff_audio ); + return 1; + } + } + return 0; +} + /** * Job initialization rountine. * Initializes fifos. @@ -496,8 +517,6 @@ static void do_job( hb_job_t * job, int cpu_count ) // 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 ); @@ -525,18 +544,6 @@ static void do_job( hb_job_t * job, int cpu_count ) audio->config.out.bitrate ); audio->config.out.bitrate = 640; } - 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. */ @@ -572,7 +579,7 @@ static void do_job( hb_job_t * job, int cpu_count ) { if (hb_audio_mixdowns[j].amixdown == audio->config.out.mixdown) { - hb_log("work: mixdown not specified, track %i setting mixdown %s", i, hb_audio_mixdowns[j].human_readable_name); + hb_log("work: mixdown not specified, track %i setting mixdown %s", audio->config.out.track, hb_audio_mixdowns[j].human_readable_name); break; } } @@ -602,7 +609,10 @@ static void do_job( hb_job_t * job, int cpu_count ) { if (hb_audio_mixdowns[j].amixdown == audio->config.out.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); + hb_log("work: sanitizing track %i mixdown %s to %s", + audio->config.out.track, + hb_audio_mixdowns[requested_mixdown_index].human_readable_name, + hb_audio_mixdowns[j].human_readable_name); break; } } @@ -623,7 +633,7 @@ static void do_job( hb_job_t * job, int cpu_count ) audio->config.out.mixdown ); hb_log( "work: bitrate not specified, track %d setting bitrate %d", - i, audio->config.out.bitrate ); + audio->config.out.track, audio->config.out.bitrate ); } /* log the requested bitrate */ @@ -645,17 +655,30 @@ static void do_job( hb_job_t * job, int cpu_count ) { /* log the output bitrate */ hb_log( "work: sanitizing track %d audio bitrate %d to %d", - i, requested_bitrate, audio->config.out.bitrate); + audio->config.out.track, requested_bitrate, + audio->config.out.bitrate); } 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( 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 ); + + audio->priv.ff_audio_list = hb_list_init(); + if ( audio->config.in.codec == HB_ACODEC_FFMPEG ) + { + if ( !check_ff_audio( title->list_audio, audio ) ) + { + audio->priv.fifo_in = hb_fifo_init( FIFO_LARGE, FIFO_LARGE_WAKE ); + } + } + else + { + audio->priv.fifo_in = hb_fifo_init( FIFO_LARGE, FIFO_LARGE_WAKE ); + } } } @@ -862,19 +885,22 @@ static void do_job( hb_job_t * job, int cpu_count ) /* * Audio Decoder Thread */ - if ( ( w = hb_codec_decoder( audio->config.in.codec ) ) == NULL ) + if ( audio->priv.fifo_in ) { - 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; + 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 ); + hb_list_add( job->list_work, w ); + } /* * Audio Encoder Thread -- cgit v1.2.3