OSDN Git Service

Flush frames from the delay queue in render. This should add an extra couple of frame...
[handbrake-jp/handbrake-jp-git.git] / libhb / encvorbis.c
index 86b8243..ce27ea0 100644 (file)
@@ -1,7 +1,7 @@
 /* $Id: encvorbis.c,v 1.6 2005/03/05 15:08:32 titer Exp $
 
    This file is part of the HandBrake source code.
-   Homepage: <http://handbrake.m0k.org/>.
+   Homepage: <http://handbrake.fr/>.
    It may be used under the terms of the GNU General Public License. */
 
 #include "hb.h"
 
 #define OGGVORBIS_FRAME_SIZE 1024
 
-struct hb_work_object_s
+int  encvorbisInit( hb_work_object_t *, hb_job_t * );
+int  encvorbisWork( hb_work_object_t *, hb_buffer_t **, hb_buffer_t ** );
+void encvorbisClose( hb_work_object_t * );
+
+hb_work_object_t hb_encvorbis =
 {
-    HB_WORK_COMMON;
+    WORK_ENCVORBIS,
+    "Vorbis encoder (libvorbis)",
+    encvorbisInit,
+    encvorbisWork,
+    encvorbisClose
+};
 
+struct hb_work_private_s
+{
     hb_job_t   * job;
-    hb_audio_t * audio;
 
     vorbis_info        vi;
     vorbis_comment     vc;
@@ -27,72 +37,122 @@ struct hb_work_object_s
     uint64_t        pts;
 
     hb_list_t     * list;
+    int           out_discrete_channels;
+    int           channel_map[6];
+    int64_t       prev_blocksize;
 };
 
-/***********************************************************************
- * Local prototypes
- **********************************************************************/
-static void Close( hb_work_object_t ** _w );
-static int  Work( hb_work_object_t * w, hb_buffer_t ** buf_in,
-                  hb_buffer_t ** buf_out );
-
-/***********************************************************************
- * hb_work_encvorbis_init
- ***********************************************************************
- *
- **********************************************************************/
-hb_work_object_t * hb_work_encvorbis_init( hb_job_t * job, hb_audio_t * audio )
+int encvorbisInit( hb_work_object_t * w, hb_job_t * job )
 {
+    hb_audio_t * audio = w->audio;
     int i;
     ogg_packet header[3];
+    struct ovectl_ratemanage2_arg  ctl_rate_arg;
 
-    hb_work_object_t * w = calloc( sizeof( hb_work_object_t ), 1 );
-    w->name  = strdup( "Vorbis encoder (libvorbis)" );
-    w->work  = Work;
-    w->close = Close;
+    hb_work_private_t * pv = calloc( 1, sizeof( hb_work_private_t ) );
+    w->private_data = pv;
+    pv->out_discrete_channels = HB_AMIXDOWN_GET_DISCRETE_CHANNEL_COUNT(audio->config.out.mixdown);
 
-    w->job   = job;
-    w->audio = audio;
+    pv->job   = job;
 
     hb_log( "encvorbis: opening libvorbis" );
 
+    /* 28kbps/channel seems to be the minimum for 6ch vorbis. */
+    int min_bitrate = 28 * pv->out_discrete_channels;
+    if (pv->out_discrete_channels > 2 && audio->config.out.bitrate < min_bitrate)
+    {
+        hb_log( "encvorbis: Selected bitrate (%d kbps) too low for %d channel audio.", audio->config.out.bitrate, pv->out_discrete_channels);
+        hb_log( "encvorbis: Resetting bitrate to %d kbps", min_bitrate);
+        /* Naughty! We shouldn't modify the audio from here. */
+        audio->config.out.bitrate = min_bitrate;
+    }
+
     /* init */
-    vorbis_info_init( &w->vi );
-    if( vorbis_encode_setup_managed( &w->vi, 2,
-          job->arate, -1, 1000 * job->abitrate, -1 ) ||
-        vorbis_encode_ctl( &w->vi, OV_ECTL_RATEMANAGE_AVG, NULL ) ||
-          vorbis_encode_setup_init( &w->vi ) )
+    vorbis_info_init( &pv->vi );
+    if( vorbis_encode_setup_managed( &pv->vi, pv->out_discrete_channels,
+          audio->config.out.samplerate, -1, 1000 * audio->config.out.bitrate, -1 ) )
+    {
+        hb_error( "encvorbis: vorbis_encode_setup_managed failed.\n" );
+        *job->die = 1;
+        return 0;
+    }
+
+    if( vorbis_encode_ctl( &pv->vi, OV_ECTL_RATEMANAGE2_GET, &ctl_rate_arg) )
     {
-        hb_log( "encvorbis: vorbis_encode_setup_managed failed" );
+        hb_log( "encvorbis: vorbis_encode_ctl( ratemanage2_get ) failed" );
+    }
+
+    ctl_rate_arg.bitrate_average_kbps = audio->config.out.bitrate;
+    ctl_rate_arg.management_active = 1;
+
+    if( vorbis_encode_ctl( &pv->vi, OV_ECTL_RATEMANAGE2_SET, &ctl_rate_arg ) ||
+          vorbis_encode_setup_init( &pv->vi ) )
+    {
+        hb_error( "encvorbis: vorbis_encode_ctl( ratemanage2_set ) OR vorbis_encode_setup_init failed.\n" );
+        *job->die = 1;
+        return 0;
     }
 
     /* add a comment */
-    vorbis_comment_init( &w->vc );
-    vorbis_comment_add_tag( &w->vc, "Encoder", "HandBrake");
+    vorbis_comment_init( &pv->vc );
+    vorbis_comment_add_tag( &pv->vc, "Encoder", "HandBrake");
+    vorbis_comment_add_tag( &pv->vc, "LANGUAGE", w->config->vorbis.language);
 
     /* set up the analysis state and auxiliary encoding storage */
-    vorbis_analysis_init( &w->vd, &w->vi);
-    vorbis_block_init( &w->vd, &w->vb);
+    vorbis_analysis_init( &pv->vd, &pv->vi);
+    vorbis_block_init( &pv->vd, &pv->vb);
 
     /* get the 3 headers */
-    vorbis_analysis_headerout( &w->vd, &w->vc,
+    vorbis_analysis_headerout( &pv->vd, &pv->vc,
                                &header[0], &header[1], &header[2] );
     for( i = 0; i < 3; i++ )
     {
-        audio->config.vorbis.headers[i] =
-            malloc( sizeof( ogg_packet ) + header[i].bytes );
-        memcpy( audio->config.vorbis.headers[i], &header[i],
+        memcpy( w->config->vorbis.headers[i], &header[i],
                 sizeof( ogg_packet ) );
-        memcpy( audio->config.vorbis.headers[i] + sizeof( ogg_packet ),
+        memcpy( w->config->vorbis.headers[i] + sizeof( ogg_packet ),
                 header[i].packet, header[i].bytes );
     }
 
-    w->input_samples = 2 * OGGVORBIS_FRAME_SIZE;
-    w->buf = malloc( w->input_samples * sizeof( float ) );
-
-    w->list = hb_list_init();
+    pv->input_samples = pv->out_discrete_channels * OGGVORBIS_FRAME_SIZE;
+    pv->buf = malloc( pv->input_samples * sizeof( float ) );
+
+    pv->list = hb_list_init();
+
+    switch (pv->out_discrete_channels) {
+        case 1:
+            pv->channel_map[0] = 0;
+            break;
+        case 6:
+            // Vorbis use the following channels map = L C R Ls Rs Lfe
+            if( audio->config.in.codec == HB_ACODEC_AC3 )
+            {
+                pv->channel_map[0] = 1;
+                pv->channel_map[1] = 2;
+                pv->channel_map[2] = 3;
+                pv->channel_map[3] = 4;
+                pv->channel_map[4] = 5;
+                pv->channel_map[5] = 0;
+            }
+            else
+            {
+                pv->channel_map[0] = 1;
+                pv->channel_map[1] = 0;
+                pv->channel_map[2] = 2;
+                pv->channel_map[3] = 3;
+                pv->channel_map[4] = 4;
+                pv->channel_map[5] = 5;
+            }
+            break;
+        default:
+            hb_log("encvorbis.c: Unable to correctly proccess %d channels, assuming stereo.", pv->out_discrete_channels);
+        case 2:
+            // Assume stereo
+            pv->channel_map[0] = 0;
+            pv->channel_map[1] = 1;
+            break;
+    }
 
-    return w;
+    return 0;
 }
 
 /***********************************************************************
@@ -100,12 +160,21 @@ hb_work_object_t * hb_work_encvorbis_init( hb_job_t * job, hb_audio_t * audio )
  ***********************************************************************
  *
  **********************************************************************/
-static void Close( hb_work_object_t ** _w )
+void encvorbisClose( hb_work_object_t * w )
 {
-    hb_work_object_t * w = *_w;
-    free( w->name );
-    free( w );
-    *_w = NULL;
+    hb_work_private_t * pv = w->private_data;
+
+    vorbis_block_clear( &pv->vb );
+    vorbis_dsp_clear( &pv->vd );
+    vorbis_comment_clear( &pv->vc );
+    vorbis_info_clear( &pv->vi );
+
+    if (pv->list)
+        hb_list_empty( &pv->list );
+
+    free( pv->buf );
+    free( pv );
+    w->private_data = NULL;
 }
 
 /***********************************************************************
@@ -115,27 +184,29 @@ static void Close( hb_work_object_t ** _w )
  **********************************************************************/
 static hb_buffer_t * Flush( hb_work_object_t * w )
 {
+    hb_work_private_t * pv = w->private_data;
     hb_buffer_t * buf;
+    int64_t     blocksize = 0;
 
-    if( vorbis_analysis_blockout( &w->vd, &w->vb ) == 1 )
+    if( vorbis_analysis_blockout( &pv->vd, &pv->vb ) == 1 )
     {
         ogg_packet op;
 
-        vorbis_analysis( &w->vb, NULL );
-        vorbis_bitrate_addblock( &w->vb );
+        vorbis_analysis( &pv->vb, NULL );
+        vorbis_bitrate_addblock( &pv->vb );
 
-        if( vorbis_bitrate_flushpacket( &w->vd, &op ) )
+        if( vorbis_bitrate_flushpacket( &pv->vd, &op ) )
         {
             buf = hb_buffer_init( sizeof( ogg_packet ) + op.bytes );
             memcpy( buf->data, &op, sizeof( ogg_packet ) );
             memcpy( buf->data + sizeof( ogg_packet ), op.packet,
                     op.bytes );
-            buf->key   = 1;
-            buf->start = w->pts; /* No exact, but who cares - the OGM
-                                    muxer doesn't use it */
-            buf->stop  = buf->start +
-                90000 * OGGVORBIS_FRAME_SIZE + w->job->arate;
-
+            blocksize = vorbis_packet_blocksize(&pv->vi, &op);
+            buf->frametype   = HB_FRAME_AUDIO;
+            buf->start = (int64_t)(vorbis_granule_time(&pv->vd, op.granulepos) * 90000);
+            buf->stop  = (int64_t)(vorbis_granule_time(&pv->vd, (pv->prev_blocksize + blocksize)/4 + op.granulepos) * 90000);
+            /* The stop time isn't accurate for the first ~3 packets, as the actual blocksize depends on the previous _and_ current packets. */
+            pv->prev_blocksize = blocksize;
             return buf;
         }
     }
@@ -150,9 +221,10 @@ static hb_buffer_t * Flush( hb_work_object_t * w )
  **********************************************************************/
 static hb_buffer_t * Encode( hb_work_object_t * w )
 {
+    hb_work_private_t * pv = w->private_data;
     hb_buffer_t * buf;
     float ** buffer;
-    int i;
+    int i, j;
 
     /* Try to extract more data */
     if( ( buf = Flush( w ) ) )
@@ -160,21 +232,23 @@ static hb_buffer_t * Encode( hb_work_object_t * w )
         return buf;
     }
 
-    if( hb_list_bytes( w->list ) < w->input_samples * sizeof( float ) )
+    if( hb_list_bytes( pv->list ) < pv->input_samples * sizeof( float ) )
     {
         return NULL;
     }
 
     /* Process more samples */
-    hb_list_getbytes( w->list, w->buf, w->input_samples * sizeof( float ),
-                      &w->pts, NULL );
-    buffer = vorbis_analysis_buffer( &w->vd, OGGVORBIS_FRAME_SIZE );
+    hb_list_getbytes( pv->list, pv->buf, pv->input_samples * sizeof( float ),
+                      &pv->pts, NULL );
+    buffer = vorbis_analysis_buffer( &pv->vd, OGGVORBIS_FRAME_SIZE );
     for( i = 0; i < OGGVORBIS_FRAME_SIZE; i++ )
     {
-        buffer[0][i] = ((float *) w->buf)[2*i]   / 32768.f;
-        buffer[1][i] = ((float *) w->buf)[2*i+1] / 32768.f;
+        for( j = 0; j < pv->out_discrete_channels; j++)
+        {
+            buffer[j][i] = ((float *) pv->buf)[(pv->out_discrete_channels * i + pv->channel_map[j])] / 32768.f;
+        }
     }
-    vorbis_analysis_wrote( &w->vd, OGGVORBIS_FRAME_SIZE );
+    vorbis_analysis_wrote( &pv->vd, OGGVORBIS_FRAME_SIZE );
 
     /* Try to extract again */
     return Flush( w );
@@ -185,12 +259,21 @@ static hb_buffer_t * Encode( hb_work_object_t * w )
  ***********************************************************************
  *
  **********************************************************************/
-static int Work( hb_work_object_t * w, hb_buffer_t ** buf_in,
-                 hb_buffer_t ** buf_out )
+int encvorbisWork( hb_work_object_t * w, hb_buffer_t ** buf_in,
+                   hb_buffer_t ** buf_out )
 {
+    hb_work_private_t * pv = w->private_data;
     hb_buffer_t * buf;
 
-    hb_list_add( w->list, *buf_in );
+    if ( (*buf_in)->size <= 0 )
+    {
+        /* EOF on input - send it downstream & say we're done */
+        *buf_out = *buf_in;
+        *buf_in = NULL;
+       return HB_WORK_DONE;
+    }
+
+    hb_list_add( pv->list, *buf_in );
     *buf_in = NULL;
 
     *buf_out = buf = Encode( w );