OSDN Git Service

fix ffmpeg multiple audio decode issue
authorjstebbins <jstebbins@b64f7644-9d1e-0410-96f1-a4d463321fa5>
Mon, 17 Jan 2011 18:57:16 +0000 (18:57 +0000)
committerjstebbins <jstebbins@b64f7644-9d1e-0410-96f1-a4d463321fa5>
Mon, 17 Jan 2011 18:57:16 +0000 (18:57 +0000)
we can now have one ffmpeg audio input track fan out to multiple
output tracks.

git-svn-id: svn://localhost/HandBrake/trunk@3753 b64f7644-9d1e-0410-96f1-a4d463321fa5

libhb/common.c
libhb/common.h
libhb/decavcodec.c
libhb/stream.c
libhb/work.c

index f1926e9..6b1d830 100644 (file)
@@ -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 );
     }
index 0dd3a2d..bd1eadd 100644 (file)
@@ -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
index ecb1d3c..609c07c 100644 (file)
@@ -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;
index 7d9b016..c90f949 100644 (file)
@@ -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;
 }
index 53ab3fe..045f5b9 100644 (file)
@@ -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