Mercurial > mplayer.hg
view xvid_vbr.c @ 33526:140525bcc32f
Fix GUI icon bug.
The GUI icon did not display properly but showed up with various distortions.
The reason was the icon mask which hadn't been put to the X server yet when
used.
The icon itself was okay, but is rendered now in a way that doesn't need a
drawable which spares creating a GTK window and destroying it right after.
The locally used GDK variables have been moved inside the function where
they are needed.
Patch with grateful support by Steaphan Greene, sgreene cs.binghamton edu.
This closes Bugzilla #582.
author | ib |
---|---|
date | Tue, 14 Jun 2011 17:51:17 +0000 |
parents | b39155e98ac3 |
children | 3ca6a7d60396 |
line wrap: on
line source
/****************************************************************************** * * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 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, 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 = 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 dependence 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 */ 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); }