diff options
author | handbrake <[email protected]> | 2006-01-14 13:07:05 +0000 |
---|---|---|
committer | handbrake <[email protected]> | 2006-01-14 13:07:05 +0000 |
commit | 5bfcc1c3cf9baed140c62c37a13c5087bbd3d5cf (patch) | |
tree | 0d940224e2a4b6cdba1c45418da88efa1b6a6e45 /core | |
parent | 5824c4979fbc54ae3d3015c07cbf6fa4aea7516d (diff) |
HandBrake 0.5.1
git-svn-id: svn://svn.handbrake.fr/HandBrake/trunk@8 b64f7644-9d1e-0410-96f1-a4d463321fa5
Diffstat (limited to 'core')
-rw-r--r-- | core/Fifo.c | 15 | ||||
-rw-r--r-- | core/HandBrake.c | 19 | ||||
-rw-r--r-- | core/Scan.c | 4 | ||||
-rw-r--r-- | core/Utils.c | 5 | ||||
-rw-r--r-- | core/XvidEnc.c | 118 | ||||
-rw-r--r-- | core/XvidVbr.c | 1648 | ||||
-rw-r--r-- | core/XvidVbr.h | 231 |
7 files changed, 1989 insertions, 51 deletions
diff --git a/core/Fifo.c b/core/Fifo.c index a8f6034c0..74658d5d8 100644 --- a/core/Fifo.c +++ b/core/Fifo.c @@ -1,4 +1,4 @@ -/* $Id: Fifo.c,v 1.2 2003/11/05 19:14:37 titer Exp $ +/* $Id: Fifo.c,v 1.3 2003/11/09 14:27:56 titer Exp $ This file is part of the HandBrake source code. Homepage: <http://handbrake.m0k.org/>. @@ -46,10 +46,14 @@ void HBBufferReAlloc( HBBuffer * b, int size ) } } -void HBBufferClose( HBBuffer ** b ) +void HBBufferClose( HBBuffer ** _b ) { - free( (*b)->data ); - (*b) = NULL; + HBBuffer * b = *_b; + + free( b->data ); + free( b ); + + *_b = NULL; } HBFifo * HBFifoInit( int capacity ) @@ -100,6 +104,7 @@ void HBFifoClose( HBFifo ** _f ) HBLockClose( &f->lock ); free( f->buffers ); free( f ); - (*_f) = NULL; + + *_f = NULL; } diff --git a/core/HandBrake.c b/core/HandBrake.c index 3f84a5757..b7670bad8 100644 --- a/core/HandBrake.c +++ b/core/HandBrake.c @@ -1,4 +1,4 @@ -/* $Id: HandBrake.c,v 1.15 2003/11/07 21:52:57 titer Exp $ +/* $Id: HandBrake.c,v 1.16 2003/11/09 21:26:52 titer Exp $ This file is part of the HandBrake source code. Homepage: <http://handbrake.m0k.org/>. @@ -501,16 +501,16 @@ void HBPosition( HBHandle * h, float position ) h->framesSinceFpsUpdate++; HBLockLock( h->lock ); - if( position - h->status.position > 0.0001 || h->frames == 2 ) + h->status.position = position; + if( h->curTitle->twoPass ) { - HBLog( "Progress: %.2f %%", 100.0 * position ); - h->status.position = position; - - if( h->curTitle->twoPass ) - h->status.pass = ( position < 0.5 ) ? 1 : 2; - else - h->status.pass = 1; + h->status.pass = ( position < 0.5 ) ? 1 : 2; + } + else + { + h->status.pass = 1; } + if( HBGetDate() - h->lastFpsUpdate > 1000000 ) { h->status.frameRate = 1000000.0 * h->framesSinceFpsUpdate / @@ -521,6 +521,7 @@ void HBPosition( HBHandle * h, float position ) ( HBGetDate() - h->beginDate ) / h->status.position / 1000000; + HBLog( "Progress: %.2f %%", position * 100 ); HBLog( "Speed: %.2f fps (average: %.2f fps, " "remaining: %02d:%02d:%02d)", h->status.frameRate, h->status.avFrameRate, diff --git a/core/Scan.c b/core/Scan.c index 83f2fc098..7a62a97e0 100644 --- a/core/Scan.c +++ b/core/Scan.c @@ -1,4 +1,4 @@ -/* $Id: Scan.c,v 1.4 2003/11/06 13:03:19 titer Exp $ +/* $Id: Scan.c,v 1.5 2003/11/09 21:28:22 titer Exp $ This file is part of the HandBrake source code. Homepage: <http://handbrake.m0k.org/>. @@ -154,7 +154,7 @@ static HBTitle * ScanTitle( HBScan * s, dvdplay_ptr vmg, int index ) continue; } - if( ( id & 0xFF ) != 0xBD ) + if( ( id & 0xF0FF ) != 0x80BD ) { HBLog( "HBScan: non-AC3 audio track detected, ignoring" ); continue; diff --git a/core/Utils.c b/core/Utils.c index 5f35ec3ae..94b3b1e07 100644 --- a/core/Utils.c +++ b/core/Utils.c @@ -1,4 +1,4 @@ -/* $Id: Utils.c,v 1.6 2003/11/06 15:51:36 titer Exp $ +/* $Id: Utils.c,v 1.7 2003/11/09 14:27:56 titer Exp $ This file is part of the HandBrake source code. Homepage: <http://handbrake.m0k.org/>. @@ -86,8 +86,7 @@ int HBPStoES( HBBuffer ** _psBuffer, HBList * esBufferList ) d[pos+2] != 0x1 || d[pos+3] != 0xBA ) { HBLog( "HBPStoES: not a PS packet (%02x%02x%02x%02x)", - d[pos] << 24, d[pos+1] << 16, - d[pos+2] << 8, d[pos+3] ); + d[pos], d[pos+1], d[pos+2], d[pos+3] ); HBBufferClose( _psBuffer ); return 0; } diff --git a/core/XvidEnc.c b/core/XvidEnc.c index 8f16ad4ba..2ca6bca6f 100644 --- a/core/XvidEnc.c +++ b/core/XvidEnc.c @@ -1,12 +1,13 @@ -/* $Id: XvidEnc.c,v 1.5 2003/11/05 19:14:37 titer Exp $ +/* $Id: XvidEnc.c,v 1.7 2003/11/09 21:26:52 titer Exp $ This file is part of the HandBrake source code. Homepage: <http://handbrake.m0k.org/>. It may be used under the terms of the GNU General Public License. */ -#include "XvidEnc.h" #include "Fifo.h" #include "Work.h" +#include "XvidEnc.h" +#include "XvidVbr.h" #include <xvid.h> @@ -20,9 +21,11 @@ struct HBXvidEnc HBHandle * handle; HBTitle * title; - void * xvid; - HBBuffer * mpeg4Buffer; - int pass; + void * xvid; + vbr_control_t xvidVbr; + XVID_ENC_FRAME frame; + HBBuffer * mpeg4Buffer; + int pass; }; HBXvidEnc * HBXvidEncInit( HBHandle * handle, HBTitle * title ) @@ -41,6 +44,18 @@ HBXvidEnc * HBXvidEncInit( HBHandle * handle, HBTitle * title ) x->title = title; x->xvid = NULL; + + x->frame.general = XVID_H263QUANT | XVID_HALFPEL | XVID_INTER4V; + x->frame.motion = PMV_EARLYSTOP16 | PMV_HALFPELREFINE16 | + PMV_EXTSEARCH16 | PMV_EARLYSTOP8 | + PMV_HALFPELREFINE8 | PMV_HALFPELDIAMOND8 | + PMV_USESQUARES16; + + x->frame.colorspace = XVID_CSP_I420; + + x->frame.quant_intra_matrix = NULL; + x->frame.quant_inter_matrix = NULL; + x->mpeg4Buffer = NULL; x->pass = 42; @@ -50,7 +65,18 @@ HBXvidEnc * HBXvidEncInit( HBHandle * handle, HBTitle * title ) void HBXvidEncClose( HBXvidEnc ** _x ) { HBXvidEnc * x = *_x; + + if( x->xvid ) + { + HBLog( "HBXvidEnc: closing libxvidcore (pass %d)", + x->pass ); + + xvid_encore( x->xvid, XVID_ENC_DESTROY, NULL, NULL); + vbrFinish( &x->xvidVbr ); + } + free( x ); + *_x = NULL; } @@ -60,7 +86,7 @@ static int XvidEncWork( HBWork * w ) HBTitle * title = x->title; HBBuffer * scaledBuffer; HBBuffer * mpeg4Buffer; - XVID_ENC_FRAME xframe; + XVID_ENC_STATS stats; int didSomething = 0; @@ -91,6 +117,15 @@ static int XvidEncWork( HBWork * w ) XVID_INIT_PARAM xinit; XVID_ENC_PARAM xparam; + if( x->xvid ) + { + HBLog( "HBXvidEnc: closing libxvidcore (pass %d)", + x->pass ); + + xvid_encore( x->xvid, XVID_ENC_DESTROY, NULL, NULL); + vbrFinish( &x->xvidVbr ); + } + x->pass = scaledBuffer->pass;; HBLog( "HBXvidEnc: opening libxvidcore (pass %d)", x->pass ); @@ -100,11 +135,11 @@ static int XvidEncWork( HBWork * w ) xparam.width = title->outWidth; xparam.height = title->outHeight; - + xparam.fincr = title->rateBase; xparam.fbase = title->rate; - xparam.rc_bitrate = title->bitrate * 1000; + xparam.rc_bitrate = title->bitrate * 1024; /* Default values should be ok */ xparam.rc_reaction_delay_factor = -1; @@ -120,51 +155,70 @@ static int XvidEncWork( HBWork * w ) } x->xvid = xparam.handle; - } - /* TODO implement 2-pass encoding */ - if( x->pass == 1 ) - { - HBPosition( x->handle, scaledBuffer->position ); - HBBufferClose( &scaledBuffer ); - return didSomething; + /* Init VBR engine */ + vbrSetDefaults( &x->xvidVbr ); + if( !x->pass ) + { + x->xvidVbr.mode = VBR_MODE_1PASS; + } + else if( x->pass == 1 ) + { + x->xvidVbr.mode = VBR_MODE_2PASS_1; + } + else + { + x->xvidVbr.mode = VBR_MODE_2PASS_2; + } + x->xvidVbr.fps = (double) title->rate / title->rateBase; + x->xvidVbr.debug = 0; + x->xvidVbr.filename = malloc( 1024 ); + memset( x->xvidVbr.filename, 0, 1024 ); + snprintf( x->xvidVbr.filename, 1023, "/tmp/HB.%d.xvid.log", + HBGetPid( x->handle ) ); + x->xvidVbr.desired_bitrate = title->bitrate * 1024; + x->xvidVbr.max_key_interval = 10 * title->rate / title->rateBase; + + vbrInit( &x->xvidVbr ); } mpeg4Buffer = HBBufferInit( title->outWidth * title->outHeight * 3 / 2 ); mpeg4Buffer->position = scaledBuffer->position; - xframe.general = XVID_H263QUANT | XVID_HALFPEL | XVID_INTER4V; - xframe.motion = PMV_EARLYSTOP16 | PMV_HALFPELREFINE16 | - PMV_EXTSEARCH16 | PMV_EARLYSTOP8 | - PMV_HALFPELREFINE8 | PMV_HALFPELDIAMOND8 | - PMV_USESQUARES16; - xframe.bitstream = mpeg4Buffer->data; + x->frame.bitstream = mpeg4Buffer->data; + x->frame.length = -1; - xframe.image = scaledBuffer->data; - xframe.colorspace = XVID_CSP_I420; + x->frame.image = scaledBuffer->data; - xframe.quant_intra_matrix = NULL; - xframe.quant_inter_matrix = NULL; - xframe.quant = 0; - xframe.intra = -1; + x->frame.quant = vbrGetQuant( &x->xvidVbr ); + x->frame.intra = vbrGetIntra( &x->xvidVbr ); - xframe.hint.hintstream = NULL; + x->frame.hint.hintstream = NULL; - if( xvid_encore( x->xvid, XVID_ENC_ENCODE, &xframe, NULL ) ) + if( xvid_encore( x->xvid, XVID_ENC_ENCODE, &x->frame, &stats ) ) { HBLog( "HBXvidEnc: xvid_encore() failed" ); } - mpeg4Buffer->size = xframe.length; - mpeg4Buffer->keyFrame = xframe.intra; + vbrUpdate( &x->xvidVbr, stats.quant, x->frame.intra, stats.hlength, + x->frame.length, stats.kblks, stats.mblks, stats.ublks ); + + mpeg4Buffer->size = x->frame.length; + mpeg4Buffer->keyFrame = x->frame.intra; /* Inform the GUI about the current position */ HBPosition( x->handle, scaledBuffer->position ); HBBufferClose( &scaledBuffer ); - x->mpeg4Buffer = mpeg4Buffer; + if( x->pass == 1 ) + { + HBBufferClose( &mpeg4Buffer ); + return didSomething; + } + + x->mpeg4Buffer = mpeg4Buffer; return didSomething; } diff --git a/core/XvidVbr.c b/core/XvidVbr.c new file mode 100644 index 000000000..ce66c3769 --- /dev/null +++ b/core/XvidVbr.c @@ -0,0 +1,1648 @@ +/****************************************************************************** + * + * XviD VBR Library + * + * Copyright (C) 2002 Edouard Gomez <[email protected]> + * + * The curve treatment algorithm is based on work done by Foxer <email?> and + * Dirk Knop <[email protected]> for the XviD vfw dynamic library. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + *****************************************************************************/ + +/* Standard Headers */ +#include <stdio.h> +#include <string.h> +#include <fcntl.h> +#include <stdlib.h> +#include <math.h> + +/* Local headers */ +#include "XvidVbr.h" + +/****************************************************************************** + * Build time constants + *****************************************************************************/ + +/* + * Portability note + * Perhaps the msvc headers define Pi with another constant name + */ +#define DEG2RAD (M_PI / 180.0) + +/* Defaults settings will be computed with the help of these constants */ +#define DEFAULT_DESIRED_SIZE 700 +#define DEFAULT_AUDIO_BITRATE 128 +#define DEFAULT_MOVIE_LENGTH 2 +#define DEFAULT_TWOPASS_BOOST 1000 +#define DEFAULT_FPS 25.0f +#define DEFAULT_CREDITS_SIZE 0 + +#define DEFAULT_XVID_DBG_FILE "xvid.dbg" +#define DEFAULT_XVID_STATS_FILE "xvid.stats" + + +/****************************************************************************** + * Local prototypes + *****************************************************************************/ + +/* Sub vbrInit cases functions */ +static vbr_init_function vbr_init_dummy; +static vbr_init_function vbr_init_2pass1; +static vbr_init_function vbr_init_2pass2; +static vbr_init_function vbr_init_fixedquant; + +/* Sub vbrGetQuant cases functions */ +static vbr_get_quant_function vbr_getquant_1pass; +static vbr_get_quant_function vbr_getquant_2pass1; +static vbr_get_quant_function vbr_getquant_2pass2; +static vbr_get_quant_function vbr_getquant_fixedquant; + +/* Sub vbrGetIntra cases functions */ +static vbr_get_intra_function vbr_getintra_1pass; +static vbr_get_intra_function vbr_getintra_2pass1; +static vbr_get_intra_function vbr_getintra_2pass2; +static vbr_get_intra_function vbr_getintra_fixedquant; + +/* Sub vbrUpdate prototypes */ +static vbr_update_function vbr_update_dummy; +static vbr_update_function vbr_update_2pass1; +static vbr_update_function vbr_update_2pass2; + +/* Sub vbrFinish cases functions */ +static vbr_finish_function vbr_finish_dummy; +static vbr_finish_function vbr_finish_2pass1; +static vbr_finish_function vbr_finish_2pass2; + +/* Is the encoder in the credits */ +#define FRAME_TYPE_NORMAL_MOVIE 0x00 +#define FRAME_TYPE_STARTING_CREDITS 0x01 +#define FRAME_TYPE_ENDING_CREDITS 0x02 + +/****************************************************************************** + * Inline utility functions + *****************************************************************************/ + +static __inline int util_frametype(vbr_control_t *state) +{ + + if(state->credits_start) { + + if(state->cur_frame >= state->credits_start_begin && + state->cur_frame < state->credits_start_end) + return(FRAME_TYPE_STARTING_CREDITS); + + } + + if(state->credits_end) { + + if(state->cur_frame >= state->credits_end_begin && + state->cur_frame < state->credits_end_end) + return(FRAME_TYPE_ENDING_CREDITS); + + } + + return(FRAME_TYPE_NORMAL_MOVIE); + + +} + +static __inline int util_creditsframes(vbr_control_t *state) +{ + + int frames = 0; + + if(state->credits_start) + frames += state->credits_start_end - state->credits_start_begin; + if(state->credits_end) + frames += state->credits_end_end - state->credits_end_begin; + + return(frames); + +} + +/****************************************************************************** + * Functions + *****************************************************************************/ + +/***************************************************************************** + * Function description : + * + * This function initialiazes the vbr_control_t with safe defaults for all + * modes. + * + * Return Values : + * = 0 + ****************************************************************************/ + +int vbrSetDefaults(vbr_control_t *state) +{ + + /* Set all the structure to zero */ + memset(state, 0, sizeof(state)); + + /* Default mode is CBR */ + state->mode = VBR_MODE_1PASS; + + /* Default statistic filename */ + state->filename = DEFAULT_XVID_STATS_FILE; + + /* + * Default is a 2hour movie on 700Mo CD-ROM + 128kbit sound track + * This represents a target bitrate of 687kbit/s + */ + state->desired_size = DEFAULT_DESIRED_SIZE*1024*1024 - + DEFAULT_MOVIE_LENGTH*3600*DEFAULT_AUDIO_BITRATE*1000/8; + state->desired_bitrate = state->desired_size*8/(DEFAULT_MOVIE_LENGTH*3600); + + /* Credits */ + state->credits_mode = VBR_CREDITS_MODE_RATE; + state->credits_start = 0; + state->credits_start_begin = 0; + state->credits_start_end = 0; + state->credits_end = 0; + state->credits_end_begin = 0; + state->credits_end_end = 0; + state->credits_quant_ratio = 20; + state->credits_fixed_quant = 20; + state->credits_quant_i = 20; + state->credits_quant_p = 20; + state->credits_start_size = DEFAULT_CREDITS_SIZE*1024*1024; + state->credits_end_size = DEFAULT_CREDITS_SIZE*1024*1024; + + /* Keyframe boost */ + state->keyframe_boost = 0; + state->kftreshold = 10; + state->kfreduction = 30; + state->min_key_interval = 1; + state->max_key_interval = (int)DEFAULT_FPS*10; + + /* Normal curve treatment */ + state->curve_compression_high = 25; + state->curve_compression_low = 10; + + /* Alt curve */ + state->use_alt_curve = 1; + state->alt_curve_type = VBR_ALT_CURVE_LINEAR; + state->alt_curve_low_dist = 90; + state->alt_curve_high_dist = 500; + state->alt_curve_min_rel_qual = 50; + state->alt_curve_use_auto = 1; + state->alt_curve_auto_str = 30; + state->alt_curve_use_auto_bonus_bias = 1; + state->alt_curve_bonus_bias = 50; + state->bitrate_payback_method = VBR_PAYBACK_BIAS; + state->bitrate_payback_delay = 250; + state->twopass_max_bitrate = DEFAULT_TWOPASS_BOOST*state->desired_bitrate; + state->twopass_max_overflow_improvement = 60; + state->twopass_max_overflow_degradation = 60; + state->max_iquant = 31; + state->min_iquant = 2; + state->max_pquant = 31; + state->min_pquant = 2; + state->fixed_quant = 3; + + state->max_framesize = (1.0/(float)DEFAULT_FPS) * state->twopass_max_bitrate / 8; + + state->fps = (float)DEFAULT_FPS; + + return(0); + +} + +/***************************************************************************** + * Function description : + * + * This function initialiaze the vbr_control_t state passed in parameter. + * + * The initialization depends on state->mode, there are 4 modes allowed. + * Each mode description is done in the README file shipped with the lib. + * + * Return values : + * + * = 0 on success + * = -1 on error + *****************************************************************************/ + +int vbrInit(vbr_control_t *state) +{ + + if(state == NULL) return(-1); + + /* Function pointers safe initialization */ + state->init = NULL; + state->getquant = NULL; + state->getintra = NULL; + state->update = NULL; + state->finish = NULL; + + if(state->debug) { + + state->debug_file = fopen(DEFAULT_XVID_DBG_FILE, "w+"); + + if(state->debug_file == NULL) + return(-1); + + fprintf(state->debug_file, "# XviD Debug output\n"); + fprintf(state->debug_file, "# quant | intra | header bytes" + "| total bytes | kblocks | mblocks | ublocks" + "| vbr overflow | vbr kf overflow" + "| vbr kf partial overflow\n\n"); + } + + /* Function pointers sub case initialization */ + switch(state->mode) { + case VBR_MODE_1PASS: + state->init = vbr_init_dummy; + state->getquant = vbr_getquant_1pass; + state->getintra = vbr_getintra_1pass; + state->update = vbr_update_dummy; + state->finish = vbr_finish_dummy; + break; + case VBR_MODE_2PASS_1: + state->init = vbr_init_2pass1; + state->getquant = vbr_getquant_2pass1; + state->getintra = vbr_getintra_2pass1; + state->update = vbr_update_2pass1; + state->finish = vbr_finish_2pass1; + break; + case VBR_MODE_FIXED_QUANT: + state->init = vbr_init_fixedquant; + state->getquant = vbr_getquant_fixedquant; + state->getintra = vbr_getintra_fixedquant; + state->update = vbr_update_dummy; + state->finish = vbr_finish_dummy; + break; + case VBR_MODE_2PASS_2: + state->init = vbr_init_2pass2; + state->getintra = vbr_getintra_2pass2; + state->getquant = vbr_getquant_2pass2; + state->update = vbr_update_2pass2; + state->finish = vbr_finish_2pass2; + break; + default: + return(-1); + } + + return(state->init(state)); + +} + +/****************************************************************************** + * Function description : + * + * This function returns an adapted quantizer according to the current vbr + * controler state + * + * Return values : + * the quantizer value (0 <= value <= 31) + * (0 is a special case, means : let XviD decide) + * + *****************************************************************************/ + +int vbrGetQuant(vbr_control_t *state) +{ + + /* Returns Zero, so XviD decides alone */ + if(state == NULL || state->getquant == NULL) return(0); + + return(state->getquant(state)); + +} + +/****************************************************************************** + * Function description : + * + * This function returns the type of the frame to be encoded next (I or P/B) + * + * Return values : + * = -1 let the XviD encoder decide wether or not the next frame is I + * = 0 no I frame + * = 1 force keyframe + * + *****************************************************************************/ + +int vbrGetIntra(vbr_control_t *state) +{ + + /* Returns -1, means let XviD decide */ + if(state == NULL || state->getintra == NULL) return(-1); + + return(state->getintra(state)); + +} + +/****************************************************************************** + * Function description : + * + * This function updates the vbr control state according to collected statistics + * from XviD core + * + * Return values : + * + * = 0 on success + * = -1 on error + *****************************************************************************/ + +int vbrUpdate(vbr_control_t *state, + int quant, + int intra, + int header_bytes, + int total_bytes, + int kblocks, + int mblocks, + int ublocks) +{ + + if(state == NULL || state->update == NULL) return(-1); + + if(state->debug && state->debug_file != NULL) { + int idx; + + fprintf(state->debug_file, "%d %d %d %d %d %d %d %d %d %d\n", + quant, intra, header_bytes, total_bytes, kblocks, + mblocks, ublocks, state->overflow, state->KFoverflow, + state->KFoverflow_partial); + + idx = quant; + + if(quant < 1) + idx = 1; + if(quant > 31) + idx = 31; + + idx--; + + state->debug_quant_count[idx]++; + + } + + return(state->update(state, quant, intra, header_bytes, total_bytes, + kblocks, mblocks, ublocks)); + +} + +/****************************************************************************** + * Function description : + * + * This function stops the vbr controller + * + * Return values : + * + * = 0 on success + * = -1 on error + *****************************************************************************/ + +int vbrFinish(vbr_control_t *state) +{ + + if(state == NULL || state->finish == NULL) return(-1); + + if(state->debug && state->debug_file != NULL) { + + int i; + + fprintf(state->debug_file, "\n\n"); + + for(i=0; i<79; i++) + fprintf(state->debug_file, "#"); + + fprintf(state->debug_file, "\n# Quantizer distribution :\n\n"); + + for(i=0;i<32; i++) { + + fprintf(state->debug_file, "# quant %d : %d\n", + i+1, + state->debug_quant_count[i]); + + } + + fclose(state->debug_file); + + } + + return(state->finish(state)); + +} + +/****************************************************************************** + * Dummy functions - Used when a mode does not need such a function + *****************************************************************************/ + +static int vbr_init_dummy(void *sstate) +{ + + vbr_control_t *state = sstate; + + state->cur_frame = 0; + + return(0); + +} + +static int vbr_update_dummy(void *state, + int quant, + int intra, + int header_bytes, + int total_bytes, + int kblocks, + int mblocks, + int ublocks) +{ + + ((vbr_control_t*)state)->cur_frame++; + + return(0); + +} + +static int vbr_finish_dummy(void *state) +{ + + return(0); + +} + +/****************************************************************************** + * 1 pass mode - XviD will do its job alone. + *****************************************************************************/ + +static int vbr_getquant_1pass(void *state) +{ + + return(0); + +} + +static int vbr_getintra_1pass(void *state) +{ + + return(-1); + +} + +/****************************************************************************** + * 2 pass mode - first pass functions + *****************************************************************************/ + +static int vbr_init_2pass1(void *sstate) +{ + + FILE *f; + vbr_control_t *state = sstate; + + /* Check the filename */ + if(state->filename == NULL || state->filename[0] == '\0') + return(-1); + + /* Initialize safe defaults for 2pass 1 */ + state->pass1_file = NULL; + state->nb_frames = 0; + state->nb_keyframes = 0; + state->cur_frame = 0; + + /* Open the 1st pass file */ + if((f = fopen(state->filename, "w+")) == NULL) + return(-1); + + /* + * The File Header + * + * The extra white spaces will be used during the vbrFinish to write + * the resulting number of frames and keyframes (10 spaces == maximum + * string length of an int on 32bit machines, i don't think anyone is + * encoding more than 4 billion frames :-) + */ + fprintf(f, "# ASCII XviD vbr stat file version %d\n#\n", VBR_VERSION); + fprintf(f, "# frames : \n"); + fprintf(f, "# keyframes : \n"); + fprintf(f, "#\n# quant | intra | header bytes | total bytes | kblocks |" + " mblocks | ublocks\n\n"); + + /* Save file pointer */ + state->pass1_file = f; + + return(0); + +} + +static int vbr_getquant_2pass1(void *state) +{ + + return(2); + +} + +static int vbr_getintra_2pass1(void *state) +{ + + return(-1); + +} + +static int vbr_update_2pass1(void *sstate, + int quant, + int intra, + int header_bytes, + int total_bytes, + int kblocks, + int mblocks, + int ublocks) + + +{ + + vbr_control_t *state = sstate; + + if(state->pass1_file == NULL) + return(-1); + + /* Writes the resulting statistics */ + fprintf(state->pass1_file, "%d %d %d %d %d %d %d\n", + quant, + intra, + header_bytes, + total_bytes, + kblocks, + mblocks, + ublocks); + + /* Update vbr control state */ + if(intra) state->nb_keyframes++; + state->nb_frames++; + state->cur_frame++; + + return(0); + +} + +static int vbr_finish_2pass1(void *sstate) +{ + + int c, i; + vbr_control_t *state = sstate; + + if(state->pass1_file == NULL) + return(-1); + + /* Goto to the file beginning */ + fseek(state->pass1_file, 0, SEEK_SET); + + /* Skip the version line and the empty line */ + c = i = 0; + do { + c = fgetc(state->pass1_file); + + if(c == EOF) return(-1); + if(c == '\n') i++; + + }while(i < 2); + + /* Prepare to write to the stream */ + fseek( state->pass1_file, 0L, SEEK_CUR ); + + /* Overwrite the frame field - safe as we have written extra spaces */ + fprintf(state->pass1_file, "# frames : %.10d\n", state->nb_frames); + + /* Overwrite the keyframe field */ + fprintf(state->pass1_file, "# keyframes : %.10d\n", + state->nb_keyframes); + + /* Close the file */ + if(fclose(state->pass1_file) != 0) + return(-1); + + return(0); + +} + +/****************************************************************************** + * 2 pass mode - 2nd pass functions (Need to be finished) + *****************************************************************************/ + +static int vbr_init_2pass2(void *sstate) +{ + + FILE *f; + int c, n, pos_firstframe, credits_frames; + long long credits1_bytes; + long long credits2_bytes; + long long desired; + long long total_bytes; + long long itotal_bytes; + long long start_curved; + long long end_curved; + double total1; + double total2; + + vbr_control_t *state = sstate; + + /* Check the filename */ + if(state->filename == NULL || state->filename[0] == '\0') + return(-1); + + /* Initialize safe defaults for 2pass 2 */ + state->pass1_file = NULL; + state->nb_frames = 0; + state->nb_keyframes = 0; + + /* Open the 1st pass file */ + if((f = fopen(state->filename, "r")) == NULL) + return(-1); + + state->pass1_file = f; + + /* Get the file version and check against current version */ + fscanf(state->pass1_file, "# ASCII XviD vbr stat file version %d\n", &n); + + if(n != VBR_VERSION) { + fclose(state->pass1_file); + state->pass1_file = NULL; + return(-1); + } + + /* Skip the blank commented line */ + c = n = 0; + do { + + c = fgetc(state->pass1_file); + + if(c == EOF) { + fclose(state->pass1_file); + state->pass1_file = NULL; + return(-1); + } + + if(c == '\n') n++; + + }while(n < 1); + + + /* Get the number of frames */ + fscanf(state->pass1_file, "# frames : %d\n", &state->nb_frames); + + /* Compute the desired size */ + state->desired_size = (long long) + (((long long)state->nb_frames * (long long)state->desired_bitrate) / + (state->fps * 8.0)); + + /* Get the number of keyframes */ + fscanf(state->pass1_file, "# keyframes : %d\n", &state->nb_keyframes); + + /* Allocate memory space for the keyframe_location array */ + if((state->keyframe_locations + = (int*)malloc((state->nb_keyframes+1)*sizeof(int))) == NULL) { + fclose(state->pass1_file); + state->pass1_file = NULL; + return(-1); + } + + /* Skip the blank commented line and the colum description */ + c = n = 0; + do { + + c = fgetc(state->pass1_file); + + if(c == EOF) { + fclose(state->pass1_file); + state->pass1_file = NULL; + return(-1); + } + + if(c == '\n') n++; + + }while(n < 2); + + /* Save position for future use */ + pos_firstframe = ftell(state->pass1_file); + + /* Read and initialize some variables */ + credits1_bytes = credits2_bytes = 0; + total_bytes = itotal_bytes = 0; + start_curved = end_curved = 0; + credits_frames = 0; + + for(state->cur_frame = c = 0; state->cur_frame<state->nb_frames; state->cur_frame++) { + + int quant, keyframe, frame_hbytes, frame_bytes; + int kblocks, mblocks, ublocks; + + fscanf(state->pass1_file, "%d %d %d %d %d %d %d\n", + &quant, &keyframe, &frame_hbytes, &frame_bytes, + &kblocks, &mblocks, &ublocks); + + /* Is the frame in the beginning credits */ + if(util_frametype(state) == FRAME_TYPE_STARTING_CREDITS) { + credits1_bytes += frame_bytes; + credits_frames++; + continue; + } + + /* Is the frame in the eding credits */ + if(util_frametype(state) == FRAME_TYPE_ENDING_CREDITS) { + credits2_bytes += frame_bytes; + credits_frames++; + continue; + } + + /* We only care about Keyframes when not in credits */ + if(keyframe) { + itotal_bytes += frame_bytes + frame_bytes * + state->keyframe_boost / 100; + total_bytes += frame_bytes * + state->keyframe_boost / 100; + state->keyframe_locations[c++] = state->cur_frame; + } + + total_bytes += frame_bytes; + + } + + /* + * Last frame is treated like an I Frame so we can dispatch overflow + * all other the last film segment + */ + state->keyframe_locations[c] = state->cur_frame; + + desired = state->desired_size; + + switch(state->credits_mode) { + case VBR_CREDITS_MODE_QUANT : + + state->movie_curve = (double) + (total_bytes - credits1_bytes - credits2_bytes) / + (desired - credits1_bytes - credits2_bytes); + + start_curved = credits1_bytes; + end_curved = credits2_bytes; + + break; + case VBR_CREDITS_MODE_SIZE: + + /* start curve = (start / start desired size) */ + state->credits_start_curve = (double) + (credits1_bytes / state->credits_start_size); + + /* end curve = (end / end desired size) */ + state->credits_end_curve = (double) + (credits2_bytes / state->credits_end_size); + + start_curved = (long long) + (credits1_bytes / state->credits_start_curve); + + end_curved = (long long) + (credits2_bytes / state->credits_end_curve); + + /* movie curve=(total-credits)/(desired_size-curved credits) */ + state->movie_curve = (double) + (total_bytes - credits1_bytes - credits2_bytes) / + (desired - start_curved - end_curved); + + break; + case VBR_CREDITS_MODE_RATE: + default: + + /* credits curve = (total/desired_size)*(100/credits_rate) */ + state->credits_start_curve = state->credits_end_curve = + ((double)total_bytes / desired) * + ((double)100 / state->credits_quant_ratio); + + start_curved = + (long long)(credits1_bytes/state->credits_start_curve); + + end_curved = + (long long)(credits2_bytes/state->credits_end_curve); + + state->movie_curve = (double) + (total_bytes - credits1_bytes - credits2_bytes) / + (desired - start_curved - end_curved); + + break; + } + + /* + * average frame size = (desired - curved credits - curved keyframes) / + * (frames - credits frames - keyframes) + */ + state->average_frame = (double) + (desired - start_curved - end_curved - + (itotal_bytes / state->movie_curve)) / + (state->nb_frames - util_creditsframes(state) - + state->nb_keyframes); + + /* Initialize alt curve parameters */ + if (state->use_alt_curve) { + + state->alt_curve_low = + state->average_frame - state->average_frame * + (double)(state->alt_curve_low_dist / 100.0); + + state->alt_curve_low_diff = + state->average_frame - state->alt_curve_low; + + state->alt_curve_high = + state->average_frame + state->average_frame * + (double)(state->alt_curve_high_dist / 100.0); + + state->alt_curve_high_diff = + state->alt_curve_high - state->average_frame; + + if (state->alt_curve_use_auto) { + + if (state->movie_curve > 1.0) { + + state->alt_curve_min_rel_qual = + (int)(100.0 - (100.0 - 100.0 / state->movie_curve) * + (double)state->alt_curve_auto_str / 100.0); + + if (state->alt_curve_min_rel_qual < 20) + state->alt_curve_min_rel_qual = 20; + } + else { + state->alt_curve_min_rel_qual = 100; + } + + } + + state->alt_curve_mid_qual = + (1.0 + (double)state->alt_curve_min_rel_qual / 100.0) / 2.0; + + state->alt_curve_qual_dev = 1.0 - state->alt_curve_mid_qual; + + if (state->alt_curve_low_dist > 100) { + + switch(state->alt_curve_type) { + case VBR_ALT_CURVE_AGGRESIVE: + /* Sine Curve (high aggressiveness) */ + state->alt_curve_qual_dev *= + 2.0 / + (1.0 + sin(DEG2RAD * (state->average_frame * 90.0 / state->alt_curve_low_diff))); + + state->alt_curve_mid_qual = + 1.0 - state->alt_curve_qual_dev * + sin(DEG2RAD * (state->average_frame * 90.0 / state->alt_curve_low_diff)); + break; + + default: + case VBR_ALT_CURVE_LINEAR: + /* Linear (medium aggressiveness) */ + state->alt_curve_qual_dev *= + 2.0 / + (1.0 + state->average_frame / state->alt_curve_low_diff); + + state->alt_curve_mid_qual = + 1.0 - state->alt_curve_qual_dev * + state->average_frame / state->alt_curve_low_diff; + + break; + + case VBR_ALT_CURVE_SOFT: + /* Cosine Curve (low aggressiveness) */ + state->alt_curve_qual_dev *= + 2.0 / + (1.0 + (1.0 - cos(DEG2RAD * (state->average_frame * 90.0 / state->alt_curve_low_diff)))); + + state->alt_curve_mid_qual = + 1.0 - state->alt_curve_qual_dev * + (1.0 - cos(DEG2RAD * (state->average_frame * 90.0 / state->alt_curve_low_diff))); + + break; + } + } + } + + /* Go to the first non credits frame stats line into file */ + fseek(state->pass1_file, pos_firstframe, SEEK_SET); + + /* Perform prepass to compensate for over/undersizing */ + total1 = total2 = 0.0; + for(state->cur_frame=0; state->cur_frame<state->nb_frames; state->cur_frame++) { + + int quant, keyframe, frame_hbytes, frame_bytes; + int kblocks, mblocks, ublocks; + + fscanf(state->pass1_file, "%d %d %d %d %d %d %d\n", + &quant, &keyframe, &frame_hbytes, &frame_bytes, + &kblocks, &mblocks, &ublocks); + + if(util_frametype(state) != FRAME_TYPE_NORMAL_MOVIE) + continue; + + if(!keyframe) { + + double dbytes = frame_bytes / state->movie_curve; + total1 += dbytes; + + if (state->use_alt_curve) { + + if (dbytes > state->average_frame) { + + if (dbytes >= state->alt_curve_high) { + total2 += dbytes * (state->alt_curve_mid_qual - state->alt_curve_qual_dev); + } + else { + + switch(state->alt_curve_type) { + case VBR_ALT_CURVE_AGGRESIVE: + + total2 += + dbytes * + (state->alt_curve_mid_qual - state->alt_curve_qual_dev * + sin(DEG2RAD * ((dbytes - state->average_frame) * 90.0 / state->alt_curve_high_diff))); + break; + default: + case VBR_ALT_CURVE_LINEAR: + + total2 += + dbytes * + (state->alt_curve_mid_qual - state->alt_curve_qual_dev * + (dbytes - state->average_frame) / state->alt_curve_high_diff); + break; + case VBR_ALT_CURVE_SOFT: + total2 += + dbytes * + (state->alt_curve_mid_qual - state->alt_curve_qual_dev * + (1.0 - cos(DEG2RAD * ((dbytes - state->average_frame) * 90.0 / state->alt_curve_high_diff)))); + } + } + } + else { + + if (dbytes <= state->alt_curve_low) { + total2 += dbytes; + } + else { + + switch(state->alt_curve_type) { + case VBR_ALT_CURVE_AGGRESIVE: + total2 += + dbytes * + (state->alt_curve_mid_qual - state->alt_curve_qual_dev * + sin(DEG2RAD * ((dbytes - state->average_frame) * 90.0 / state->alt_curve_low_diff))); + break; + default: + case VBR_ALT_CURVE_LINEAR: + total2 += + dbytes * + (state->alt_curve_mid_qual - state->alt_curve_qual_dev * + (dbytes - state->average_frame) / state->alt_curve_low_diff); + break; + case VBR_ALT_CURVE_SOFT: + total2 += + dbytes * + (state->alt_curve_mid_qual + state->alt_curve_qual_dev * + (1.0 - cos(DEG2RAD * ((dbytes - state->average_frame) * 90.0 / state->alt_curve_low_diff)))); + } + } + } + } + else { + if (dbytes > state->average_frame) { + total2 += + ((double)dbytes + + (state->average_frame - dbytes) * + state->curve_compression_high / 100.0); + } + else { + total2 += + ((double)dbytes + + (state->average_frame - dbytes) * + state->curve_compression_low / 100.0); + } + } + } + } + + state->curve_comp_scale = total1 / total2; + + if (state->use_alt_curve) { + + double curve_temp, dbytes; + int newquant, percent; + int oldquant = 1; + + if (state->alt_curve_use_auto_bonus_bias) + state->alt_curve_bonus_bias = state->alt_curve_min_rel_qual; + + state->curve_bias_bonus = + (total1 - total2) * (double)state->alt_curve_bonus_bias / + (100.0 * (double)(state->nb_frames - util_creditsframes(state) - state->nb_keyframes)); + state->curve_comp_scale = + ((total1 - total2) * (1.0 - (double)state->alt_curve_bonus_bias / 100.0) + total2) / + total2; + + + for (n=1; n <= (int)(state->alt_curve_high*2) + 1; n++) { + dbytes = n; + if (dbytes > state->average_frame) + { + if (dbytes >= state->alt_curve_high) { + curve_temp = dbytes * (state->alt_curve_mid_qual - state->alt_curve_qual_dev); + } + else { + switch(state->alt_curve_type) { + case VBR_ALT_CURVE_AGGRESIVE: + curve_temp = dbytes * (state->alt_curve_mid_qual - state->alt_curve_qual_dev * + sin(DEG2RAD * ((dbytes - state->average_frame) * 90.0 / state->alt_curve_high_diff))); + break; + default: + case VBR_ALT_CURVE_LINEAR: + curve_temp = dbytes * (state->alt_curve_mid_qual - state->alt_curve_qual_dev * + (dbytes - state->average_frame) / state->alt_curve_high_diff); + break; + case VBR_ALT_CURVE_SOFT: + curve_temp = dbytes * (state->alt_curve_mid_qual - state->alt_curve_qual_dev * + (1.0 - cos(DEG2RAD * ((dbytes - state->average_frame) * 90.0 / state->alt_curve_high_diff)))); + } + } + } + else { + if (dbytes <= state->alt_curve_low) { + curve_temp = dbytes; + } + else { + switch(state->alt_curve_type) { + case VBR_ALT_CURVE_AGGRESIVE: + curve_temp = dbytes * (state->alt_curve_mid_qual - state->alt_curve_qual_dev * + sin(DEG2RAD * ((dbytes - state->average_frame) * 90.0 / state->alt_curve_low_diff))); + break; + default: + case VBR_ALT_CURVE_LINEAR: + curve_temp = dbytes * (state->alt_curve_mid_qual - state->alt_curve_qual_dev * + (dbytes - state->average_frame) / state->alt_curve_low_diff); + break; + case VBR_ALT_CURVE_SOFT: + curve_temp = dbytes * (state->alt_curve_mid_qual + state->alt_curve_qual_dev * + (1.0 - cos(DEG2RAD * ((dbytes - state->average_frame) * 90.0 / state->alt_curve_low_diff)))); + } + } + } + + if (state->movie_curve > 1.0) + dbytes *= state->movie_curve; + + newquant = (int)(dbytes * 2.0 / (curve_temp * state->curve_comp_scale + state->curve_bias_bonus)); + if (newquant > 1) + { + if (newquant != oldquant) + { + oldquant = newquant; + percent = (int)((n - state->average_frame) * 100.0 / state->average_frame); + } + + } + + } + + } + + state->overflow = 0; + state->KFoverflow = 0; + state->KFoverflow_partial = 0; + state->KF_idx = 1; + + for (n=0 ; n < 32 ; n++) { + state->quant_error[n] = 0.0; + state->quant_count[n] = 0; + } + + state->curve_comp_error = 0.0; + state->last_quant = 0; + + /* + * Above this frame size limit, normal vbr rules will not apply + * This means : + * 1 - Quant can de/increase more than -/+2 between 2 frames + * 2 - Leads to artifacts because of 1 + */ + state->max_framesize = state->twopass_max_bitrate/state->fps; + + /* Get back to the beginning of frame statistics */ + fseek(state->pass1_file, pos_firstframe, SEEK_SET); + + /* + * Small hack : We have to get next frame stats before the + * getintra/quant calls + * User clients update the data when they call vbrUpdate + * we are just bypassing this because we don't have to update + * the overflow and so on... + */ + { + + /* Fake vars */ + int next_hbytes, next_kblocks, next_mblocks, next_ublocks; + + fscanf(state->pass1_file, "%d %d %d %d %d %d %d\n", + &state->pass1_quant, &state->pass1_intra, &next_hbytes, + &state->pass1_bytes, &next_kblocks, &next_mblocks, + &next_ublocks); + + } + + /* Initialize the frame counter */ + state->cur_frame = 0; + state->last_keyframe = 0; + + return(0); + +} + +static int vbr_getquant_2pass2(void *sstate) +{ + + int quant; + int intra; + int bytes1, bytes2; + int overflow; + int capped_to_max_framesize = 0; + int KFdistance, KF_min_size; + vbr_control_t *state = sstate; + + bytes1 = state->pass1_bytes; + overflow = state->overflow / 8; + /* To shut up gcc warning */ + bytes2 = bytes1; + + + if (state->pass1_intra) + { + overflow = 0; + } + + if (util_frametype(state) != FRAME_TYPE_NORMAL_MOVIE) { + + + switch (state->credits_mode) { + case VBR_CREDITS_MODE_QUANT : + if (state->credits_quant_i != state->credits_quant_p) { + quant = state->pass1_intra ? + state->credits_quant_i: + state->credits_quant_p; + } + else { + quant = state->credits_quant_p; + } + + state->bytes1 = bytes1; + state->bytes2 = bytes1; + state->desired_bytes2 = bytes1; + return(quant); + default: + case VBR_CREDITS_MODE_RATE : + case VBR_CREDITS_MODE_SIZE : + if(util_frametype(state) == FRAME_TYPE_STARTING_CREDITS) + bytes2 = (int)(bytes1 / state->credits_start_curve); + else + bytes2 = (int)(bytes1 / state->credits_end_curve); + break; + } + } + else { + /* Foxer: apply curve compression outside credits */ + double dbytes, curve_temp; + + bytes2 = bytes1; + + if (state->pass1_intra) + dbytes = ((int)(bytes2 + bytes2 * state->keyframe_boost / 100)) / + state->movie_curve; + else + dbytes = bytes2 / state->movie_curve; + + /* spread the compression error accross payback_delay frames */ + if (state->bitrate_payback_method == VBR_PAYBACK_BIAS) { + bytes2 = (int)(state->curve_comp_error / state->bitrate_payback_delay); + } + else { + bytes2 = (int)(state->curve_comp_error * dbytes / + state->average_frame / state->bitrate_payback_delay); + + if (labs(bytes2) > fabs(state->curve_comp_error)) + bytes2 = (int)state->curve_comp_error; + } + + state->curve_comp_error -= bytes2; + + if (state->use_alt_curve) { + + if (!state->pass1_intra) { + + if (dbytes > state->average_frame) { + if (dbytes >= state->alt_curve_high) + curve_temp = dbytes * (state->alt_curve_mid_qual - state->alt_curve_qual_dev); + else { + switch(state->alt_curve_type) { + case VBR_ALT_CURVE_AGGRESIVE: + curve_temp = dbytes * (state->alt_curve_mid_qual - state->alt_curve_qual_dev * + sin(DEG2RAD * ((dbytes - state->average_frame) * 90.0 / state->alt_curve_high_diff))); + break; + default: + case VBR_ALT_CURVE_LINEAR: + curve_temp = dbytes * (state->alt_curve_mid_qual - state->alt_curve_qual_dev * + (dbytes - state->average_frame) / state->alt_curve_high_diff); + break; + case VBR_ALT_CURVE_SOFT: + curve_temp = dbytes * (state->alt_curve_mid_qual - state->alt_curve_qual_dev * + (1.0 - cos(DEG2RAD * ((dbytes - state->average_frame) * 90.0 / state->alt_curve_high_diff)))); + } + } + } + else { + if (dbytes <= state->alt_curve_low) + curve_temp = dbytes; + else { + switch(state->alt_curve_type) { + case VBR_ALT_CURVE_AGGRESIVE: + curve_temp = dbytes * (state->alt_curve_mid_qual - state->alt_curve_qual_dev * + sin(DEG2RAD * ((dbytes - state->average_frame) * 90.0 / state->alt_curve_low_diff))); + break; + default: + case VBR_ALT_CURVE_LINEAR: + curve_temp = dbytes * (state->alt_curve_mid_qual - state->alt_curve_qual_dev * + (dbytes - state->average_frame) / state->alt_curve_low_diff); + break; + case VBR_ALT_CURVE_SOFT: + curve_temp = dbytes * (state->alt_curve_mid_qual + state->alt_curve_qual_dev * + (1.0 - cos(DEG2RAD * ((dbytes - state->average_frame) * 90.0 / state->alt_curve_low_diff)))); + } + } + } + + curve_temp = curve_temp * state->curve_comp_scale + state->curve_bias_bonus; + + bytes2 += ((int)curve_temp); + state->curve_comp_error += curve_temp - ((int)curve_temp); + + } + else { + state->curve_comp_error += dbytes - ((int)dbytes); + bytes2 += ((int)dbytes); + } + } + else if ((state->curve_compression_high + state->curve_compression_low) && + !state->pass1_intra) { + + if (dbytes > state->average_frame) { + curve_temp = state->curve_comp_scale * + ((double)dbytes + (state->average_frame - dbytes) * + state->curve_compression_high / 100.0); + } + else { + curve_temp = state->curve_comp_scale * + ((double)dbytes + (state->average_frame - dbytes) * + state->curve_compression_low / 100.0); + } + + bytes2 += ((int)curve_temp); + state->curve_comp_error += curve_temp - ((int)curve_temp); + } + else { + state->curve_comp_error += dbytes - ((int)dbytes); + bytes2 += ((int)dbytes); + } + + /* cap bytes2 to first pass size, lowers number of quant=1 frames */ + if (bytes2 > bytes1) { + state->curve_comp_error += bytes2 - bytes1; + bytes2 = bytes1; + } + else if (bytes2 < 1) { + state->curve_comp_error += --bytes2; + bytes2 = 1; + } + } + + state->desired_bytes2 = bytes2; + + /* Ugly dependance between getquant and getintra */ + intra = state->getintra(state); + + if(intra) { + + KFdistance = state->keyframe_locations[state->KF_idx] - + state->keyframe_locations[state->KF_idx - 1]; + + if (KFdistance < state->kftreshold) { + KFdistance = KFdistance - state->min_key_interval; + + if (KFdistance >= 0) { + + KF_min_size = bytes2 * (100 - state->kfreduction) / 100; + if (KF_min_size < 1) + KF_min_size = 1; + + bytes2 = KF_min_size + (bytes2 - KF_min_size) * KFdistance / + (state->kftreshold - state->min_key_interval); + + if (bytes2 < 1) + bytes2 = 1; + } + } + } + + /* + * Foxer: scale overflow in relation to average size, so smaller frames don't get + * too much/little bitrate + */ + overflow = (int)((double)overflow * bytes2 / state->average_frame); + + /* Foxer: reign in overflow with huge frames */ + if (labs(overflow) > labs(state->overflow)) { + overflow = state->overflow; + } + + /* Foxer: make sure overflow doesn't run away */ + if(overflow > bytes2 * state->twopass_max_overflow_improvement / 100) { + bytes2 += (overflow <= bytes2) ? bytes2 * state->twopass_max_overflow_improvement / 100 : + overflow * state->twopass_max_overflow_improvement / 100; + } + else if(overflow < bytes2 * state->twopass_max_overflow_degradation / -100) { + bytes2 += bytes2 * state->twopass_max_overflow_degradation / -100; + } + else { + bytes2 += overflow; + } + + if(bytes2 > state->max_framesize) { + capped_to_max_framesize = 1; + bytes2 = state->max_framesize; + } + + if(bytes2 < 1) { + bytes2 = 1; + } + + state->bytes1 = bytes1; + state->bytes2 = bytes2; + + /* very 'simple' quant<->filesize relationship */ + quant = state->pass1_quant * bytes1 / bytes2; + + if(quant < 1) + quant = 1; + else if(quant > 31) + quant = 31; + else if(!state->pass1_intra) { + + /* Foxer: aid desired quantizer precision by accumulating decision error */ + state->quant_error[quant] += ((double)(state->pass1_quant * bytes1) / bytes2) - quant; + + if (state->quant_error[quant] >= 1.0) { + state->quant_error[quant] -= 1.0; + quant++; + } + } + + /* we're done with credits */ + if(util_frametype(state) != FRAME_TYPE_NORMAL_MOVIE) { + return(quant); + } + + if(intra) { + + if (quant < state->min_iquant) + quant = state->min_iquant; + if (quant > state->max_iquant) + quant = state->max_iquant; + } + else { + + if(quant > state->max_pquant) + quant = state->max_pquant; + if(quant < state->min_pquant) + quant = state->min_pquant; + + /* subsequent frame quants can only be +- 2 */ + if(state->last_quant && capped_to_max_framesize == 0) { + if (quant > state->last_quant + 2) + quant = state->last_quant + 2; + if (quant < state->last_quant - 2) + quant = state->last_quant - 2; + } + } + + return(quant); + +} + +static int vbr_getintra_2pass2(void *sstate) +{ + + int intra; + vbr_control_t *state = sstate; + + + /* Get next intra state (fetched by update) */ + intra = state->pass1_intra; + + /* During credits, XviD will decide itself */ + if(util_frametype(state) != FRAME_TYPE_NORMAL_MOVIE) { + + + switch(state->credits_mode) { + default: + case VBR_CREDITS_MODE_RATE : + case VBR_CREDITS_MODE_SIZE : + intra = -1; + break; + case VBR_CREDITS_MODE_QUANT : + /* Except in this case */ + if (state->credits_quant_i == state->credits_quant_p) + intra = -1; + break; + } + + } + + /* Force I Frame when max_key_interval is reached */ + if((state->cur_frame - state->last_keyframe) > state->max_key_interval) + intra = 1; + + /* + * Force P or B Frames for frames whose distance is less than the + * requested minimum + */ + if((state->cur_frame - state->last_keyframe) < state->min_key_interval) + intra = 0; + + + /* Return the given intra mode except for first frame */ + return((state->cur_frame==0)?1:intra); + +} + +static int vbr_update_2pass2(void *sstate, + int quant, + int intra, + int header_bytes, + int total_bytes, + int kblocks, + int mblocks, + int ublocks) + + +{ + + + int next_hbytes, next_kblocks, next_mblocks, next_ublocks; + int tempdiv; + + vbr_control_t *state = sstate; + + /* + * We do not depend on getintra/quant because we have the real results + * from the xvid core + */ + + if (util_frametype(state) == FRAME_TYPE_NORMAL_MOVIE) { + + state->quant_count[quant]++; + + if (state->pass1_intra) { + + state->overflow += state->KFoverflow; + state->KFoverflow = state->desired_bytes2 - total_bytes; + + tempdiv = (state->keyframe_locations[state->KF_idx] - + state->keyframe_locations[state->KF_idx - 1]); + + /* redistribute correctly (by koepi) */ + if (tempdiv > 1) { + /* non-consecutive keyframes */ + state->KFoverflow_partial = state->KFoverflow / + (tempdiv - 1); + } + else { + state->overflow += state->KFoverflow; + state->KFoverflow = 0; + state->KFoverflow_partial = 0; + } + state->KF_idx++; + + } + else { + state->overflow += state->desired_bytes2 - total_bytes + + state->KFoverflow_partial; + state->KFoverflow -= state->KFoverflow_partial; + } + } + else { + + state->overflow += state->desired_bytes2 - total_bytes; + state->overflow += state->KFoverflow; + state->KFoverflow = 0; + state->KFoverflow_partial = 0; + } + + /* Save old quant */ + state->last_quant = quant; + + /* Update next frame data */ + fscanf(state->pass1_file, "%d %d %d %d %d %d %d\n", + &state->pass1_quant, &state->pass1_intra, &next_hbytes, + &state->pass1_bytes, &next_kblocks, &next_mblocks, + &next_ublocks); + + /* Save the last Keyframe pos */ + if(intra) + state->last_keyframe = state->cur_frame; + + /* Ok next frame */ + state->cur_frame++; + + return(0); + +} + +static int vbr_finish_2pass2(void *sstate) +{ + + vbr_control_t *state = sstate; + + if(state->pass1_file == NULL) + return(-1); + + /* Close the file */ + if(fclose(state->pass1_file) != 0) + return(-1); + + /* Free the memory */ + if(state->keyframe_locations) + free(state->keyframe_locations); + + return(0); + +} + + +/****************************************************************************** + * Fixed quant mode - Most of the functions will be dummy functions + *****************************************************************************/ + +static int vbr_init_fixedquant(void *sstate) +{ + + vbr_control_t *state = sstate; + + if(state->fixed_quant < 1) + state->fixed_quant = 1; + + if(state->fixed_quant > 31) + state->fixed_quant = 31; + + state->cur_frame = 0; + + return(0); + +} + +static int vbr_getquant_fixedquant(void *sstate) +{ + + vbr_control_t *state = sstate; + + /* Credits' frame ? */ + if(util_frametype(state) != FRAME_TYPE_NORMAL_MOVIE) { + + int quant; + + switch(state->credits_mode) { + case VBR_CREDITS_MODE_RATE: + quant = state->fixed_quant * state->credits_quant_ratio; + break; + case VBR_CREDITS_MODE_QUANT: + quant = state->credits_fixed_quant; + break; + default: + quant = state->fixed_quant; + + } + + return(quant); + + } + + /* No credit frame - return fixed quant */ + return(state->fixed_quant); + +} + +static int vbr_getintra_fixedquant(void *state) +{ + + return(-1); + +} diff --git a/core/XvidVbr.h b/core/XvidVbr.h new file mode 100644 index 000000000..ea9d3b703 --- /dev/null +++ b/core/XvidVbr.h @@ -0,0 +1,231 @@ +/****************************************************************************** + * + * XviD VBR Library + * + * Copyright (C) 2002 Edouard Gomez <[email protected]> + * + * The curve treatment algorithm is based on work done by Foxer <email?> and + * Dirk Knop <[email protected]> for the XviD vfw dynamic library. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + *****************************************************************************/ + +#ifndef __XVID_VBR_H__ +#define __XVID_VBR_H__ + +#define VBR_VERSION 0 + +/****************************************************************************** + * Function types used in the vbr controler + *****************************************************************************/ + +typedef int (vbr_init_function)(void *state); +typedef vbr_init_function *vbr_init_function_ptr; + +typedef int (vbr_get_quant_function)(void *state); +typedef vbr_get_quant_function *vbr_get_quant_function_ptr; + +typedef int (vbr_get_intra_function)(void *state); +typedef vbr_get_intra_function *vbr_get_intra_function_ptr; + +typedef int (vbr_update_function)(void *state, + int quant, + int intra, + int header_bytes, + int total_bytes, + int kblocks, + int mblocks, + int ublocks); +typedef vbr_update_function *vbr_update_function_ptr; + +typedef int (vbr_finish_function)(void *state); +typedef vbr_finish_function *vbr_finish_function_ptr; + +/****************************************************************************** + * The VBR CONTROLER structure - the spin of the library + *****************************************************************************/ + +typedef struct _vbr_control_t +{ + + /* All modes - specifies what VBR algorithm has to be used */ + int mode; + + /* All modes - specifies what fps the movie uses */ + float fps; + + /* All modes */ + int debug; + + /* + * For VBR_MODE_2PASS_1/2 - specifies from/to what file the vbr + * controller has to write/read stats + */ + char *filename; + + /* For VBR_MODE_2PASS_2 - Target size */ + int desired_bitrate; + + /* For VBR_MODE_2PASS_2 - Credits parameters */ + int credits_mode; + int credits_start; + int credits_start_begin; + int credits_start_end; + int credits_end; + int credits_end_begin; + int credits_end_end; + int credits_quant_ratio; + int credits_fixed_quant; + int credits_quant_i; + int credits_quant_p; + int credits_start_size; + int credits_end_size; + + /* For VBR_MODE_2PASS_2 - keyframe parameters */ + int keyframe_boost; + int kftreshold; + int kfreduction; + int min_key_interval; + int max_key_interval; + + /* For VBR_MODE_2PASS_2 - Normal curve */ + int curve_compression_high; + int curve_compression_low; + + /* For VBR_MODE_2PASS_2 - Alternate curve parameters */ + int use_alt_curve; + int alt_curve_type; + int alt_curve_low_dist; + int alt_curve_high_dist; + int alt_curve_min_rel_qual; + int alt_curve_use_auto; + int alt_curve_auto_str; + int alt_curve_use_auto_bonus_bias; + int alt_curve_bonus_bias; + int bitrate_payback_method; + int bitrate_payback_delay; + int max_iquant; + int min_iquant; + int max_pquant; + int min_pquant; + int twopass_max_bitrate; + int twopass_max_overflow_improvement; + int twopass_max_overflow_degradation; + + /* + * For VBR_MODE_FIXED_QUANT - the quantizer that has to be used for all + * frames + */ + int fixed_quant; + + /* ----------- Internal data - Do not modify ----------- */ + void *debug_file; + void *pass1_file; + + long long desired_size; + + int cur_frame; + int nb_frames; + int nb_keyframes; + + int *keyframe_locations; + int last_keyframe; + + double credits_start_curve; + double credits_end_curve; + double movie_curve; + double average_frame; + double alt_curve_low; + double alt_curve_low_diff; + double alt_curve_high; + double alt_curve_high_diff; + double alt_curve_mid_qual; + double alt_curve_qual_dev; + double curve_bias_bonus; + double curve_comp_scale; + double curve_comp_error; + + int pass1_quant; + int pass1_intra; + int pass1_bytes; + + int bytes1; + int bytes2; + int desired_bytes2; + int max_framesize; + int last_quant; + int quant_count[32]; + double quant_error[32]; + + int overflow; + int KFoverflow; + int KFoverflow_partial; + int KF_idx; + + int debug_quant_count[32]; + + /* ----------- Internal data - do not modify ----------- */ + vbr_init_function_ptr init; + vbr_get_quant_function_ptr getquant; + vbr_get_intra_function_ptr getintra; + vbr_update_function_ptr update; + vbr_finish_function_ptr finish; + +}vbr_control_t; + +/****************************************************************************** + * Constants + *****************************************************************************/ + +/* Constants for the mode member */ +#define VBR_MODE_1PASS 0x01 +#define VBR_MODE_2PASS_1 0x02 +#define VBR_MODE_2PASS_2 0x04 +#define VBR_MODE_FIXED_QUANT 0x08 + +/* Constants for the credits mode */ +#define VBR_CREDITS_MODE_RATE 0x01 +#define VBR_CREDITS_MODE_QUANT 0x02 +#define VBR_CREDITS_MODE_SIZE 0x04 + +/* Alternate curve treatment types */ +#define VBR_ALT_CURVE_SOFT 0x01 +#define VBR_ALT_CURVE_LINEAR 0x02 +#define VBR_ALT_CURVE_AGGRESIVE 0x04 + +/* Payback modes */ +#define VBR_PAYBACK_BIAS 0x01 +#define VBR_PAYBACK_PROPORTIONAL 0x02 + +/****************************************************************************** + * VBR API + *****************************************************************************/ + +extern int vbrSetDefaults(vbr_control_t *state); +extern int vbrInit(vbr_control_t *state); +extern int vbrGetQuant(vbr_control_t *state); +extern int vbrGetIntra(vbr_control_t *state); +extern int vbrUpdate(vbr_control_t *state, + int quant, + int intra, + int header_bytes, + int total_bytes, + int kblocks, + int mblocks, + int ublocks); +extern int vbrFinish(vbr_control_t *state); + +#endif |