summaryrefslogtreecommitdiffstats
path: root/core
diff options
context:
space:
mode:
authorhandbrake <[email protected]>2006-01-14 13:07:05 +0000
committerhandbrake <[email protected]>2006-01-14 13:07:05 +0000
commit5bfcc1c3cf9baed140c62c37a13c5087bbd3d5cf (patch)
tree0d940224e2a4b6cdba1c45418da88efa1b6a6e45 /core
parent5824c4979fbc54ae3d3015c07cbf6fa4aea7516d (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.c15
-rw-r--r--core/HandBrake.c19
-rw-r--r--core/Scan.c4
-rw-r--r--core/Utils.c5
-rw-r--r--core/XvidEnc.c118
-rw-r--r--core/XvidVbr.c1648
-rw-r--r--core/XvidVbr.h231
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