OSDN Git Service

Fix a hang in sync
[handbrake-jp/handbrake-jp-git.git] / libhb / enctheora.c
index 3773b70..042262c 100644 (file)
@@ -1,9 +1,10 @@
 /* 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"
-#include "theora/theora.h"
+#include "theora/codec.h"
+#include "theora/theoraenc.h"
 
 int  enctheoraInit( hb_work_object_t *, hb_job_t * );
 int  enctheoraWork( hb_work_object_t *, hb_buffer_t **, hb_buffer_t ** );
@@ -20,78 +21,188 @@ hb_work_object_t hb_enctheora =
 
 struct hb_work_private_s
 {
-    hb_job_t * job;
+    hb_job_t    * job;
 
-    theora_state theora;
+    th_enc_ctx    * ctx;
+
+    FILE          * file;
+    unsigned char   stat_buf[80];
+    int             stat_read;
+    int             stat_fill;
 };
 
 int enctheoraInit( hb_work_object_t * w, hb_job_t * job )
 {
+    int keyframe_frequency, log_keyframe, ret;
     hb_work_private_t * pv = calloc( 1, sizeof( hb_work_private_t ) );
     w->private_data = pv;
 
     pv->job = job;
 
-    theora_info ti;
-    theora_comment tc;
+    if( job->pass != 0 && job->pass != -1 )
+    {
+        char filename[1024];
+        memset( filename, 0, 1024 );
+        hb_get_tempory_filename( job->h, filename, "theroa.log" );
+        if ( job->pass == 1 )
+        {
+            pv->file = fopen( filename, "wb" );
+        }
+        else
+        {
+            pv->file = fopen( filename, "rb" );
+        }
+    }
+
+    th_info ti;
+    th_comment tc;
     ogg_packet op;
-    theora_info_init( &ti );
+    th_info_init( &ti );
+
+    /* Frame width and height need to be multiples of 16 */
+    ti.pic_width = job->width;
+    ti.pic_height = job->height;
+    ti.frame_width = (job->width + 0xf) & ~0xf;
+    ti.frame_height = (job->height + 0xf) & ~0xf;
+    ti.pic_x = ti.pic_y = 0;
 
-    ti.width = ti.frame_width = job->width;
-    ti.height = ti.frame_height = job->height;
-    ti.offset_x = ti.offset_y = 0;
     ti.fps_numerator = job->vrate;
     ti.fps_denominator = job->vrate_base;
-    if (job->pixel_ratio)
+    if( job->anamorphic.mode )
     {
-        ti.aspect_numerator = job->pixel_aspect_width;
-        ti.aspect_denominator = job->pixel_aspect_height;
+        ti.aspect_numerator = job->anamorphic.par_width;
+        ti.aspect_denominator = job->anamorphic.par_height;
     }
     else
     {
         ti.aspect_numerator = ti.aspect_denominator = 1;
     }
-    ti.colorspace = OC_CS_UNSPECIFIED;
-    ti.pixelformat = OC_PF_420;
-    ti.keyframe_auto_p = 1;
-    ti.keyframe_frequency = (job->vrate / job->vrate_base) + 1;
-    ti.keyframe_frequency_force = (10 * job->vrate / job->vrate_base) + 1;
-    /* From encoder_example.c */
-    ti.quick_p = 1;
-    ti.dropframes_p = 0;
-    ti.keyframe_auto_threshold = 80;
-    ti.keyframe_mindistance = 8;
-    ti.noise_sensitivity = 1;
-    ti.sharpness = 0;
-    if (job->vquality < 0.0 || job->vquality > 1.0)
+    ti.colorspace = TH_CS_UNSPECIFIED;
+    ti.pixel_fmt = TH_PF_420;
+    if (job->vquality < 0.0)
     {
         ti.target_bitrate = job->vbitrate * 1000;
-        ti.keyframe_data_target_bitrate = job->vbitrate * 1000 * 1.5;
         ti.quality = 0;
     }
     else
     {
         ti.target_bitrate = 0;
-        ti.quality = 63 * job->vquality;
+        
+        if( job->vquality > 0 && job->vquality < 1 )
+        {
+            ti.quality = 63 * job->vquality;            
+        }
+        else
+        {
+            ti.quality = job->vquality;
+        }
+    }
+
+    if ( job->pass == 2 && !job->cfr )
+    {
+        /* Even though the framerate might be different due to VFR,
+           we still want the same keyframe intervals as the 1st pass,
+           so the 1st pass stats won't conflict on frame decisions.    */
+        hb_interjob_t * interjob = hb_interjob_get( job->h );
+        keyframe_frequency = ( 10 * interjob->vrate / interjob->vrate_base ) + 1;
+    }
+    else
+    {
+        int fps = job->vrate / job->vrate_base;
+
+        /* adjust +1 when fps has remainder to bump
+           { 23.976, 29.976, 59.94 } to { 24, 30, 60 } */
+        if (job->vrate % job->vrate_base)
+            fps += 1;
+
+        keyframe_frequency = fps * 10;
     }
+    int tmp = keyframe_frequency - 1;
+    for (log_keyframe = 0; tmp; log_keyframe++)
+        tmp >>= 1;
+        
+    hb_log("theora: keyint: %i", keyframe_frequency);
+
+    ti.keyframe_granule_shift = log_keyframe;
 
-    theora_encode_init( &pv->theora, &ti );
-    theora_info_clear( &ti );
+    pv->ctx = th_encode_alloc( &ti );
+    th_info_clear( &ti );
 
-    theora_encode_header( &pv->theora, &op );
+    ret = th_encode_ctl(pv->ctx, TH_ENCCTL_SET_KEYFRAME_FREQUENCY_FORCE,
+                        &keyframe_frequency, sizeof(keyframe_frequency));
+    if( ret < 0 )
+    {
+        hb_log("theora: Could not set keyframe interval to %d", keyframe_frequency);
+    }
+
+    /* Set "soft target" rate control which improves quality at the
+     * expense of solid bitrate caps */
+    int arg = TH_RATECTL_CAP_UNDERFLOW;
+    ret = th_encode_ctl(pv->ctx, TH_ENCCTL_SET_RATE_FLAGS, &arg, sizeof(arg));
+    if( ret < 0 )
+    {
+        hb_log("theora: Could not set soft ratecontrol");
+    }
+    if( job->pass != 0 && job->pass != -1 )
+    {
+        arg = keyframe_frequency * 7 >> 1;
+        ret = th_encode_ctl(pv->ctx, TH_ENCCTL_SET_RATE_BUFFER, &arg, sizeof(arg));
+        if( ret < 0 )
+        {
+            hb_log("theora: Could not set rate control buffer");
+        }
+    }
+
+    if( job->pass == 1 )
+    {
+        unsigned char *buffer;
+        int bytes;
+        bytes = th_encode_ctl(pv->ctx, TH_ENCCTL_2PASS_OUT, &buffer, sizeof(buffer));
+        if( bytes < 0 )
+        {
+            hb_error("Could not set up the first pass of two-pass mode.\n");
+            hb_error("Did you remember to specify an estimated bitrate?\n");
+            return 1;
+        }
+        if( fwrite( buffer, 1, bytes, pv->file ) < bytes )
+        {
+            hb_error("Unable to write to two-pass data file.\n");
+            return 1;
+        }
+        fflush( pv->file );
+    }
+    if( job->pass == 2 )
+    {
+        /* Enable the second pass here.
+         * We make this call just to set the encoder into 2-pass mode, because
+         * by default enabling two-pass sets the buffer delay to the whole file
+         * (because there's no way to explicitly request that behavior).
+         * If we waited until we were actually encoding, it would overwite our
+         * settings.*/
+        hb_log("enctheora: init 2nd pass");
+        if( th_encode_ctl( pv->ctx, TH_ENCCTL_2PASS_IN, NULL, 0) < 0)
+        {
+            hb_log("theora: Could not set up the second pass of two-pass mode.");
+            return 1;
+        }
+    }
+
+    th_comment_init( &tc );
+
+    th_encode_flushheader( pv->ctx, &tc, &op );
     memcpy(w->config->theora.headers[0], &op, sizeof(op));
     memcpy(w->config->theora.headers[0] + sizeof(op), op.packet, op.bytes );
 
-    theora_comment_init(&tc);
-    theora_encode_comment(&tc,&op);
+    th_encode_flushheader( pv->ctx, &tc, &op );
     memcpy(w->config->theora.headers[1], &op, sizeof(op));
     memcpy(w->config->theora.headers[1] + sizeof(op), op.packet, op.bytes );
-    free(op.packet);
 
-    theora_encode_tables(&pv->theora, &op);
+    th_encode_flushheader( pv->ctx, &tc, &op );
     memcpy(w->config->theora.headers[2], &op, sizeof(op));
     memcpy(w->config->theora.headers[2] + sizeof(op), op.packet, op.bytes );
 
+    th_comment_clear( &tc );
+
     return 0;
 }
 
@@ -103,8 +214,13 @@ int enctheoraInit( hb_work_object_t * w, hb_job_t * job )
 void enctheoraClose( hb_work_object_t * w )
 {
     hb_work_private_t * pv = w->private_data;
-    /* TODO: Free alloc'd */
 
+    th_encode_free( pv->ctx );
+
+    if( pv->file )
+    {
+        fclose( pv->file );
+    }
     free( pv );
     w->private_data = NULL;
 }
@@ -120,46 +236,145 @@ int enctheoraWork( hb_work_object_t * w, hb_buffer_t ** buf_in,
     hb_work_private_t * pv = w->private_data;
     hb_job_t * job = pv->job;
     hb_buffer_t * in = *buf_in, * buf;
-    yuv_buffer yuv;
+    th_ycbcr_buffer ycbcr;
     ogg_packet op;
-    static int last_p = 0;
 
-    memset(&op, 0, sizeof(op));
-    memset(&yuv, 0, sizeof(yuv));
+    int frame_width, frame_height;
 
-    /* If this is the last empty frame, we're done */
-    if(!in->data)
+    if ( in->size <= 0 )
     {
-        if (!last_p)
+        // EOF on input - send it downstream & say we're done.
+        // XXX may need to flush packets via a call to
+        //  th_encode_packetout( pv->ctx, 1, &op );
+        // but we don't have a timestamp to put on those packets so we
+        // drop them for now.
+        *buf_out = in;
+        *buf_in = NULL;
+        th_encode_packetout( pv->ctx, 1, &op );
+        if( job->pass == 1 )
         {
-            last_p++;
-            goto finish;
+            unsigned char *buffer;
+            int bytes;
+
+            bytes = th_encode_ctl(pv->ctx, TH_ENCCTL_2PASS_OUT, 
+                                  &buffer, sizeof(buffer));
+            if( bytes < 0 )
+            {
+                fprintf(stderr,"Could not read two-pass data from encoder.\n");
+                return HB_WORK_DONE;
+            }
+            fseek( pv->file, 0, SEEK_SET );
+            if( fwrite( buffer, 1, bytes, pv->file ) < bytes)
+            {
+                fprintf(stderr,"Unable to write to two-pass data file.\n");
+                return HB_WORK_DONE;
+            }
+            fflush( pv->file );
         }
-       *buf_out        = NULL;
-       return HB_WORK_DONE;
+        return HB_WORK_DONE;
     }
 
-    yuv.y_width = job->width;
-    yuv.y_height = job->height;
-    yuv.y_stride = job->width;
+    if( job->pass == 2 )
+    {
+        for(;;)
+        {
+            int bytes, size, ret;
+            /*Ask the encoder how many bytes it would like.*/
+            bytes = th_encode_ctl( pv->ctx, TH_ENCCTL_2PASS_IN, NULL, 0 );
+            if( bytes < 0 )
+            {
+                hb_error("Error requesting stats size in second pass.");
+                *job->die = 1;
+                return HB_WORK_DONE;
+            }
+
+            /*If it's got enough, stop.*/
+            if( bytes == 0 ) break;
+
+            /*Read in some more bytes, if necessary.*/
+            if( bytes > pv->stat_fill - pv->stat_read )
+                size = bytes - (pv->stat_fill - pv->stat_read);
+            else
+                size = 0;
+            if( size > 80 - pv->stat_fill )
+                size = 80 - pv->stat_fill;
+            if( size > 0 &&
+                fread( pv->stat_buf+pv->stat_fill, 1, size, pv->file ) < size )
+            {
+                hb_error("Could not read frame data from two-pass data file!");
+                *job->die = 1;
+                return HB_WORK_DONE;
+            }
+            pv->stat_fill += size;
+
+            /*And pass them off.*/
+            if( bytes > pv->stat_fill - pv->stat_read )
+                bytes = pv->stat_fill - pv->stat_read;
+            ret = th_encode_ctl( pv->ctx, TH_ENCCTL_2PASS_IN, 
+                                 pv->stat_buf+pv->stat_read, bytes);
+            if( ret < 0 )
+            {
+                hb_error("Error submitting pass data in second pass.");
+                *job->die = 1;
+                return HB_WORK_DONE;
+            }
+            /*If the encoder consumed the whole buffer, reset it.*/
+            if( ret >= pv->stat_fill - pv->stat_read )
+                pv->stat_read = pv->stat_fill = 0;
+            /*Otherwise remember how much it used.*/
+            else 
+                pv->stat_read += ret;
+        }
+    }
+    memset(&op, 0, sizeof(op));
+    memset(&ycbcr, 0, sizeof(ycbcr));
+
+    frame_width = (job->width + 0xf) & ~0xf;
+    frame_height = (job->height + 0xf) & ~0xf;
 
-    yuv.uv_width = job->width / 2;
-    yuv.uv_height = job->height / 2;
-    yuv.uv_stride = job->width / 2;
+    // Y
+    ycbcr[0].width = frame_width;
+    ycbcr[0].height = frame_height;
+    ycbcr[0].stride = job->width;
 
-    yuv.y = in->data;
-    yuv.u = in->data + job->width * job->height;
-    yuv.v = in->data + job->width * job->height * 5/4;
+    // CbCr decimated by factor of 2 in both width and height
+    ycbcr[1].width  = ycbcr[2].width  = (frame_width + 1) / 2;
+    ycbcr[1].height = ycbcr[2].height = (frame_height + 1) / 2;
+    ycbcr[1].stride = ycbcr[2].stride = (job->width + 1) / 2;
 
-    theora_encode_YUVin(&pv->theora, &yuv);
+    ycbcr[0].data = in->data;
+    ycbcr[1].data = ycbcr[0].data + (ycbcr[0].stride * job->height);
+    ycbcr[2].data = ycbcr[1].data + (ycbcr[1].stride * ((job->height+1)/2));
 
-finish:
-    theora_encode_packetout(&pv->theora, last_p, &op);
+    th_encode_ycbcr_in( pv->ctx, ycbcr );
+
+    if( job->pass == 1 )
+    {
+        unsigned char *buffer;
+        int bytes;
+
+        bytes = th_encode_ctl(pv->ctx, TH_ENCCTL_2PASS_OUT, 
+                              &buffer, sizeof(buffer));
+        if( bytes < 0 )
+        {
+            fprintf(stderr,"Could not read two-pass data from encoder.\n");
+            *job->die = 1;
+            return HB_WORK_DONE;
+        }
+        if( fwrite( buffer, 1, bytes, pv->file ) < bytes)
+        {
+            fprintf(stderr,"Unable to write to two-pass data file.\n");
+            *job->die = 1;
+            return HB_WORK_DONE;
+        }
+        fflush( pv->file );
+    }
+    th_encode_packetout( pv->ctx, 0, &op );
 
     buf = hb_buffer_init( op.bytes + sizeof(op) );
     memcpy(buf->data, &op, sizeof(op));
     memcpy(buf->data + sizeof(op), op.packet, op.bytes);
-    buf->frametype = ( theora_packet_iskeyframe(&op) ) ? HB_FRAME_KEY : HB_FRAME_REF;
+    buf->frametype = ( th_packet_iskeyframe(&op) ) ? HB_FRAME_KEY : HB_FRAME_REF;
     buf->start = in->start;
     buf->stop  = in->stop;