--- plugin_2pass1.c 2003/03/23 04:03:01 1.1.2.1 +++ plugin_2pass1.c 2010/03/09 10:00:14 1.4 @@ -1,41 +1,52 @@ /****************************************************************************** * - * XviD Bit Rate Controller Library - * - VBR 2 pass bitrate controler implementation - + * Xvid Bit Rate Controller Library + * - VBR 2 pass bitrate controler implementation - * - * Copyright (C) 2002 Edouard Gomez + * Copyright (C) 2002-2003 Edouard Gomez * - * The curve treatment algorithm is the one implemented by Foxer and - * Dirk Knop for the XviD vfw dynamic library. + * The curve treatment algorithm is the one implemented by Foxer and + * Dirk Knop 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 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. + * 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 + * 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_2pass1.c,v 1.1.2.1 2003/03/23 04:03:01 suxen_drol Exp $ + * $Id: plugin_2pass1.c,v 1.4 2010/03/09 10:00:14 Isibaar Exp $ * *****************************************************************************/ #include +#include /* errno var (or function with recent libc) */ +#include /* strerror() */ #include "../xvid.h" #include "../image/image.h" +/* This preprocessor constant controls wheteher or not, first pass is done + * using fast ME routines to speed up the 2pass process at the expense of + * less precise first pass stats */ +#define FAST1PASS +#define FAST1PASS_QPEL_TOO + + /* context struct */ typedef struct { FILE * stat_file; + + double fq_error; } rc_2pass1_t; @@ -46,27 +57,58 @@ rc_2pass1_t * rc; /* check filename */ - if (param->filename == NULL || param->filename[0] == '\0') + if ((param->filename == NULL) || + (param->filename != NULL && param->filename[0] == '\0')) return XVID_ERR_FAIL; /* allocate context struct */ if((rc = malloc(sizeof(rc_2pass1_t))) == NULL) return(XVID_ERR_MEMORY); - /* Initialize safe defaults for 2pass 1 */ + /* Initialize safe defaults for 2pass 1 */ rc->stat_file = NULL; /* Open the 1st pass file */ - if((rc->stat_file = fopen(param->filename, "w+")) == NULL) + if((rc->stat_file = fopen(param->filename, "w+b")) == NULL) return(XVID_ERR_FAIL); + /* I swear xvidcore isn't buggy, but when using mencoder+xvid4 i observe + * this weird bug. + * + * Symptoms: The stats file grows until it's fclosed, but at this moment + * a large part of the file is filled by 0x00 bytes w/o any + * reasonable cause. The stats file is then completly unusable + * + * So far, i think i found "the why": + * - take a MPEG stream containing 2 sequences (concatenate 2 MPEG files + * together) + * - Encode this MPEG file + * + * It should trigger the bug + * + * I think this is caused by some kind of race condition on mencoder module + * start/stop. + * - mencoder encodes the first sequence + * + xvid4 module opens xvid-twopass.stats and writes stats in it. + * - mencoder detects the second sequence and initialize a second + * module and stops the old encoder + * + new xvid4 module opens a new xvid-twopass.stats, old xvid4 + * module closes it + * + * This is IT, got a racing condition. + * Unbuffered IO, may help ... */ + setbuf(rc->stat_file, NULL); + /* * The File Header */ - /* fprintf(rc->stat_file, "# XviD 2pass stat file\n"); - fprintf(rc->stat_file, "version %i.%i.%i\n",XVID_MAJOR(XVID_VERSION), XVID_MINOR(XVID_VERSION), XVID_PATCH(XVID_VERSION)); - fprintf(rc->stat_file, "start\n"); - fprintf(rc->stat_file, "type quantizer length kblocks mblocks ublocks\n"); */ + fprintf(rc->stat_file, "# XviD 2pass stat file (core version %d.%d.%d)\n", + XVID_VERSION_MAJOR(XVID_VERSION), + XVID_VERSION_MINOR(XVID_VERSION), + XVID_VERSION_PATCH(XVID_VERSION)); + fprintf(rc->stat_file, "# Please do not modify this file\n\n"); + + rc->fq_error = 0; *handle = rc; return(0); @@ -75,28 +117,81 @@ static int rc_2pass1_destroy(rc_2pass1_t * rc, xvid_plg_destroy_t * destroy) { - //fprintf(rc->stat_file, "stop\n"); - fclose(rc->stat_file); - - free(rc); + if (rc->stat_file) { + if (fclose(rc->stat_file) == EOF) { + DPRINTF(XVID_DEBUG_RC, "Error closing stats file (%s)", strerror(errno)); + } + } + rc->stat_file = NULL; /* Just a paranoid reset */ + free(rc); /* as the container structure is freed anyway */ return(0); } static int rc_2pass1_before(rc_2pass1_t * rc, xvid_plg_data_t * data) { - data->quant = 2; - data->type = XVID_TYPE_AUTO; - return 0; + if (data->quant <= 0) { + if (data->zone && data->zone->mode == XVID_ZONE_QUANT) { + /* We disable no options in quant zones, as their implementation is + * based on the fact we do first pass exactly the same way as the + * second one to have exact zone size */ + rc->fq_error += (double)data->zone->increment / (double)data->zone->base; + data->quant = (int)rc->fq_error; + rc->fq_error -= data->quant; + } else { + data->quant = 2; + +#ifdef FAST1PASS + /* Given the fact our 2pass algorithm is based on very simple + * rules, we can disable some options that are too CPU intensive + * and do not provide the 2nd pass any benefit */ + + /* First disable some motion flags */ + data->motion_flags &= ~XVID_ME_CHROMA_PVOP; + data->motion_flags &= ~XVID_ME_CHROMA_BVOP; + data->motion_flags &= ~XVID_ME_USESQUARES16; + data->motion_flags &= ~XVID_ME_ADVANCEDDIAMOND16; + data->motion_flags &= ~XVID_ME_EXTSEARCH16; + + /* And enable fast replacements */ + data->motion_flags |= XVID_ME_FAST_MODEINTERPOLATE; + data->motion_flags |= XVID_ME_SKIP_DELTASEARCH; + data->motion_flags |= XVID_ME_FASTREFINE16; + data->motion_flags |= XVID_ME_BFRAME_EARLYSTOP; + + /* Now VOP flags (no fast replacements) */ + data->vop_flags &= ~XVID_VOP_MODEDECISION_RD; + data->vop_flags &= ~XVID_VOP_RD_BVOP; + data->vop_flags &= ~XVID_VOP_FAST_MODEDECISION_RD; + data->vop_flags &= ~XVID_VOP_TRELLISQUANT; + data->vop_flags &= ~XVID_VOP_INTER4V; + data->vop_flags &= ~XVID_VOP_HQACPRED; + + /* Finnaly VOL flags + * + * NB: Qpel cannot be disable because this option really changes + * too much the texture data compressibility, and thus the + * second pass gets confused by too much impredictability + * of frame sizes, and actually hurts quality */ +#ifdef FAST1PASS_QPEL_TOO + /* or maybe we can disable it after all? */ + data->vol_flags &= ~XVID_VOL_QUARTERPEL; +#endif + data->vol_flags &= ~XVID_VOL_GMC; +#endif + } + } + return(0); } static int rc_2pass1_after(rc_2pass1_t * rc, xvid_plg_data_t * data) { char type; + xvid_enc_stats_t *stats = &data->stats; /* Frame type in ascii I/P/B */ - switch(data->type) { + switch(stats->type) { case XVID_TYPE_IVOP: type = 'i'; break; @@ -115,13 +210,14 @@ /* write the resulting statistics */ - fprintf(rc->stat_file, "%c %d %d %d %d %d\n", - type, - data->quant, - data->length, - data->kblks, - data->mblks, - data->ublks); + fprintf(rc->stat_file, "%c %d %d %d %d %d %d\n", + type, + stats->quant, + stats->kblks, + stats->mblks, + stats->ublks, + stats->length, + stats->hlength); return(0); } @@ -133,6 +229,7 @@ switch(opt) { case XVID_PLG_INFO : + case XVID_PLG_FRAME : return 0; case XVID_PLG_CREATE :