--- plugin_fixed.c 2003/03/23 04:03:01 1.1 +++ plugin_fixed.c 2004/03/22 22:36:24 1.2 @@ -0,0 +1,309 @@ +/***************************************************************************** + * + * XviD VBR Library + * - Fixed quantizer controller implementation / Deprecated code - + * + * Copyright (C) 2002-2003 Edouard Gomez + * + * 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 + * + * $Id: plugin_fixed.c,v 1.2 2004/03/22 22:36:24 edgomez Exp $ + * + ****************************************************************************/ + + +#include "../xvid.h" +#include "../image/image.h" + + +typedef struct +{ + int32_t quant_increment; + int32_t quant_base; + + /* Number of quantizers in the *quant distribution */ + int nquant; + + /* Quantizer distribution */ + int *quant; +} rc_fixed_t; + + +static int gcd(int a, int b) +{ + int r; + + while(b) { + r = a % b; + a = b; + b = r; + } + + return(a); +} + + +static int +distribute_quants(int **quants, int lquant, int n_lquant, int n_hquant) +{ + + int i, n, _gcd; + int nlow, nhigh; + int low, high; + int q, r; + int a,b,min, max; + int m, M; + int *dist; + + /* Low quant */ + low = lquant; + + /* High quant is just low quant + 1 */ + high = low + 1; + + /* How much low quants we have to distribute */ + nlow = n_lquant; + + /* How much high quants we have to distribute */ + nhigh = n_hquant; + + /* Simplify the pattern, respecting the current ratio nhigh/nlow */ + _gcd = gcd( nlow, nhigh); + nlow = nlow / _gcd; + nhigh = nhigh / _gcd; + + /* Allocate the distributuion array */ + if((*quants = (int*)malloc((nhigh + nlow)*sizeof(int))) == NULL) + return (-1); + + /* Validate distribution size */ + n = nlow + nhigh; + + /* + * Our goal now is to find packet length so we distribute the quants + * the best we can. The solution i chose is quite simple. + * + * Let's 'max' be max(nlow, nhigh) + * Let's 'min' be min(nlow, nhigh) + * + * Then if we compute the euclidian division of 'max' by 'min', the + * quotient 'q' represents the length of 'max type quant' packets + * length. Let's call 'r' the remaining part of the euclidian division. + * + * We could stop there and have something like this: + * M == quant type of the quantity 'max' + * m == quant type of the quantity 'min' + * + * (MM.........Mm)*(MMMMMM...M) + * '----------' '--------' + * '----|------' |-> repeated 'r' times + * || + * ||--> repeated 'min' times + * |---> repeated 'q' times' + * + * + * Let's look if we have precision quants. + * + * We have 'min' packets of (q+1) length + * We have also a packet of length 'r' + * So : min*(q+1) + r == (min*q + r) + min == max + min == n + * + * Ok this is good :-) + * + * Now let's complicate things a bit :-))) + * + * We will distribute the last M quantizers all over the distribution + * array. To achieve that, we build packets of (q+2) quantizers containing + * qM + 1m as usual plus 1M from the last remaining M quantizer packet and + * usual packets of (q+1) with qM and 1m. + * + * So we will have this kind of distribution: + * + * (MM.........Mm)*(MMMM...Mm) + * '----------' '------' + * '----|------' '---|---' + * || ||--> repeated 'min-r' times + * || |---> repeated 'q' times + * ||--> repeated 'r' times + * |---> repeated 'q+1' times + * + * + * Let's check if it's still right: + * + * We'll have ((q+1)*r + q*(min-r))M quants == (q*r + r + q*min - q*r)M + * == (q*min + r)M + * == (max)M it's good + * And: (r + min - r)m quantizers == (min)m it's good too) + * + * NB: if the fixed quantizer is an integer then we have min == 0, so we + * must take care of it apart (else the euclidian division leads to + * an arithmetic signal - division by zero) + * + */ + + + max = MAX(nlow, nhigh); + min = MIN(nlow, nhigh); + + /* Euclidian division */ + if(min == 0) { + q = 0; + r = max; + } else { + q = max/min; + r = max%min; + } + + /* How much packets of (q+1)M quantizers + 1m quantizer */ + a = r; + + /* How much packets of (q)M quantizers + 1m quantizer */ + b = min - r; + + /* First we set M */ + M = (max==nhigh)?high:low; + + /* + * Then as we can't guarantee that max != min, we are forced to set + * the other one according to the first value. We can't use the first + * formula with s/max/min/ + */ + m = (M==high)?low:high; + + /* + * Ok now we know everything we have to know to distribute those funny + * quantizers. What about just doing it ? + */ + + dist = *quants; + + /* Distribute big packets */ + for(i=0; iparam; + rc_fixed_t * rc; + int quant_low; + int nquant_low, nquant_high; + + /* allocate context struct */ + if((rc = malloc(sizeof(rc_fixed_t))) == NULL) + return(XVID_ERR_MEMORY); + + /* Copy data to private area */ + rc->quant_increment = param->quant_increment; + rc->quant_base = param->quant_base; + + /* Cut down the precision up to 1/precision */ + quant_low = rc->quant_increment/rc->quant_base; + + /* Force [1..31] range */ + if(quant_low < 1) { + quant_low = 1; + rc->quant_base = 1; + rc->quant_increment = quant_low; + } else if(quant_low > 30) { + quant_low = 31; + rc->quant_base = 1; + rc->quant_increment = quant_low; + } + + /* How much low quants we have to distribute) */ + nquant_low = rc->quant_base*(quant_low+1) - rc->quant_increment; + nquant_high = rc->quant_base - nquant_low; + + /* Distribute the quantizers */ + rc->nquant = distribute_quants(&rc->quant, + quant_low, + nquant_low, + nquant_high); + + if(rc->quant == NULL) { + free(rc); + return(XVID_ERR_MEMORY); + } + + *handle = rc; + return(0); +} + + +static int rc_fixed_destroy(rc_fixed_t * rc, xvid_plg_destroy_t * destroy) +{ + free(rc); + return(0); +} + + +static int rc_fixed_before(rc_fixed_t * rc, xvid_plg_data_t * data) +{ + data->quant = rc->quant[data->frame_num%rc->nquant]; + data->type = XVID_TYPE_AUTO; + return 0; +} + + +int xvid_plugin_fixed(void * handle, int opt, void * param1, void * param2) +{ + switch(opt) + { + case XVID_PLG_INFO : + return 0; + + case XVID_PLG_CREATE : + return rc_fixed_create((xvid_plg_create_t*)param1, param2); + + case XVID_PLG_DESTROY : + return rc_fixed_destroy((rc_fixed_t*)handle, (xvid_plg_destroy_t*)param1); + + case XVID_PLG_BEFORE : + return rc_fixed_before((rc_fixed_t*)handle, (xvid_plg_data_t*)param1); + + case XVID_PLG_AFTER : + return 0; + } + + return XVID_ERR_FAIL; +}