mirror of https://github.com/mpv-player/mpv
1650 lines
41 KiB
C
1650 lines
41 KiB
C
/******************************************************************************
|
|
*
|
|
* XviD VBR Library
|
|
*
|
|
* Copyright (C) 2002 Edouard Gomez <ed.gomez@wanadoo.fr>
|
|
*
|
|
* The curve treatment algorithm is based on work done by Foxer <email?> and
|
|
* Dirk Knop <dknop@gwdg.de> 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 "xvid_vbr.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, ftell( state->pass1_file ), SEEK_SET );
|
|
|
|
/* 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;
|
|
|
|
/* Compensate AVI overhead */
|
|
desired = state->desired_size - state->nb_frames*24;
|
|
|
|
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);
|
|
|
|
}
|