X-Git-Url: http://git.osdn.jp/view?a=blobdiff_plain;f=libhb%2Fenctheora.c;h=042262c76150338f435cd55a7304ee629d29ba5c;hb=033e32de9c380f54c7d1362a3979da205ebc3a29;hp=59414687c2c650496fc0a331ee3939a87bf9645a;hpb=2707a4c05f84b3846e4e77ca4e9850cc61ffca10;p=handbrake-jp%2Fhandbrake-jp-git.git diff --git a/libhb/enctheora.c b/libhb/enctheora.c index 59414687..042262c7 100644 --- a/libhb/enctheora.c +++ b/libhb/enctheora.c @@ -3,7 +3,8 @@ 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;