--- trunk/xvidcore/examples/xvid_stat.c 2002/06/14 13:29:07 209 +++ trunk/xvidcore/examples/xvid_stat.c 2002/12/18 20:48:25 728 @@ -1,150 +1,660 @@ -/************************************************************************** +/***************************************************************************** * - * XVID MPEG-4 VIDEO CODEC - Example for encoding and decoding + * XVID MPEG-4 VIDEO CODEC + * - Console based test application - * - * 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. + * Copyright(C) 2002 Christoph Lampert * - * 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 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. * - * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. * - *************************************************************************/ + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id: xvid_stat.c,v 1.17 2002-12-18 20:48:25 edgomez Exp $ + * + ****************************************************************************/ -/************************************************************************ - * - * PSNR and Speed test routine for XviD using the XviD-API - * (C) Christoph Lampert, 2002/04/13 +/***************************************************************************** + * Application notes : * * A sequence of YUV pics in PGM file format is encoded and decoded * The speed is measured and PSNR of decoded picture is calculated. * * The program is plain C and needs no libraries except for libxvidcore, - * and maths-lib ,so with UN*X you simply compile by + * and maths-lib. * - * gcc xvid_stat.c -lxvidcore -lm -o xvid_stat + * Usage : xvid_stat [OPTIONS] + * Options : + * -w integer : frame width ([1.2048]) + * -h integer : frame height ([1.2048]) + * -b integer : target bitrate (>0 | default=900kbit) + * -f float : target framerate (>0) + * -i string : input filename (default=stdin) + * -t integer : input data type (yuv=0, pgm=1) + * -n integer : number of frames to encode + * -q integer : quality ([0..5]) + * -d boolean : save decoder output (0 False*, !=0 True) + * -m boolean : save mpeg4 raw stream (0 False*, !=0 True) + * -mv integer : Hinted Motion Estimation (0 none, 1 get hints, 2 set hints) + * -help : prints this help message + * -quant integer : fixed quantizer (disables -b setting) + * (* means default) * - * Run without or with illegal parameters, then PGM input input is read - * from stdin. - * - * Parameters are: xvid_stat XDIM YDIM QUALITY BITRATE/QUANTIZER FRAMERATE - * - * if XDIM or YDIM are illegal (e.g. 0), they are ignored and input is - * considered to be PGM. Otherwise (X and Y both greater than 0) raw YUV - * is expected, as e.g. the standard MPEG test-files, like "foreman" - * - * 0 <= QUALITY <= 6 (default 5) + * An input file named "stdin" is treated as standard input stream. * - * BITRATE is in kbps (default 900), - * if BITRATE<32, then value is taken is fixed QUANTIZER - * - * FRAMERATE is a float (with or without decimal dot), default is 25.00 * - * input/output and m4v-output is saved, if corresponding flags are set - * - * PGM input must in a very specific format, see read_pgmheader - * it can be generated e.g. from MPEG2 by mpeg2dec -o pgmpipe - * - ************************************************************************/ - -/************************************************************************ - * - * For EXAMPLES how to use this, see the seperate file xvid_stat.examples + * PGM input must be in a very specific format, basically it pgm file must + * contain Y plane first just like usual P5 pgm files, and then U and V + * planes are stored just after Y plane so y dimension is y*3/2 in reality * - ************************************************************************/ + * See read_pgmheader for more details. + * + * Such a PGM file can be generated from MPEG2 by # mpeg2dec -o pgmpipe + * + ****************************************************************************/ #include #include -#include // needed for log10 -#include // only needed for gettimeofday +#include +#include +#ifndef _MSC_VER +#include +#else +#include +#endif -#include "..\src\xvid.h" /* comes with XviD */ +#include "xvid.h" -int motion_presets[7] = { - 0, // Q 0 - PMV_EARLYSTOP16, // Q 1 - PMV_EARLYSTOP16, // Q 2 - PMV_EARLYSTOP16 | PMV_HALFPELREFINE16, // Q 3 - PMV_EARLYSTOP16 | PMV_HALFPELREFINE16, // Q 4 - PMV_EARLYSTOP16 | PMV_HALFPELREFINE16 | PMV_EARLYSTOP8 // Q 5 - | PMV_HALFPELREFINE8, - PMV_EARLYSTOP16 | PMV_HALFPELREFINE16 | PMV_EXTSEARCH16 // Q 6 - | PMV_USESQUARES16 | PMV_EARLYSTOP8 | PMV_HALFPELREFINE8 - }; - -int general_presets[7] = { - XVID_H263QUANT, /* or use XVID_MPEGQUANT */ // Q 0 - XVID_MPEGQUANT, // Q 1 - XVID_H263QUANT, // Q 2 - XVID_H263QUANT | XVID_HALFPEL, // Q 3 - XVID_H263QUANT | XVID_HALFPEL | XVID_INTER4V, // Q 4 - XVID_H263QUANT | XVID_HALFPEL | XVID_INTER4V, // Q 5 - XVID_H263QUANT | XVID_HALFPEL | XVID_INTER4V }; // Q 6 +/**************************************************************************** + * Prototypes + ***************************************************************************/ + +/* Prints program usage message */ +static void usage(); + +/* Statistical functions */ +static double msecond(); +static double absdistq(int x, int y, + unsigned char* buf1, int stride1, + unsigned char* buf2, int stride2); +static double PSNR(int x, int y, + unsigned char* buf1, int stride1, + unsigned char* buf2, int stride2); + +/* PGM related functions */ +static int read_pgmheader(FILE* handle); +static int read_pgmdata(FILE* handle, unsigned char *image); +static int read_yuvdata(FILE* handle, unsigned char *image); +static int write_pgm(char *filename, unsigned char *image); + +/* Encoder related functions */ +static int enc_init(int use_assembler); +static int enc_stop(); +static int enc_main(unsigned char* image, unsigned char* bitstream, + unsigned char* hints_buffer, + long *streamlength, long* frametype, long* hints_size); + +/* Decoder related functions */ +static int dec_stop(); +static int dec_main(unsigned char *m4v_buffer, unsigned char *out_buffer, + int m4v_size); +static int dec_init(int use_assembler); + +/***************************************************************************** + * Quality presets + ****************************************************************************/ + +static int const motion_presets[7] = { + 0, /* Q 0 */ + PMV_EARLYSTOP16, /* Q 1 */ + PMV_EARLYSTOP16, /* Q 2 */ + PMV_EARLYSTOP16 | PMV_HALFPELREFINE16, /* Q 3 */ + PMV_EARLYSTOP16 | PMV_HALFPELREFINE16, /* Q 4 */ + PMV_EARLYSTOP16 | PMV_HALFPELREFINE16 | PMV_EARLYSTOP8 | /* Q 5 */ + PMV_HALFPELREFINE8, + PMV_EARLYSTOP16 | PMV_HALFPELREFINE16 | PMV_EXTSEARCH16 | /* Q 6 */ + PMV_USESQUARES16 | PMV_EARLYSTOP8 | PMV_HALFPELREFINE8 +}; + +static int const general_presets[7] = { + XVID_H263QUANT, /* Q 0 */ + XVID_MPEGQUANT, /* Q 1 */ + XVID_H263QUANT, /* Q 2 */ + XVID_H263QUANT | XVID_HALFPEL, /* Q 3 */ + XVID_H263QUANT | XVID_HALFPEL | XVID_INTER4V, /* Q 4 */ + XVID_H263QUANT | XVID_HALFPEL | XVID_INTER4V, /* Q 5 */ + XVID_H263QUANT | XVID_HALFPEL | XVID_INTER4V /* Q 6 */ +}; -/* my default values for encoding */ +/***************************************************************************** + * Command line global variables + ****************************************************************************/ + +/* Maximum number of frames to encode */ +#define ABS_MAXFRAMENR 9999 + +/* HINTMODEs */ +#define HINT_MODE_NONE 0 +#define HINT_MODE_GET 1 +#define HINT_MODE_SET 2 +#define HINT_FILE "hints.mv" + +static int ARG_BITRATE = 900; +static int ARG_QUANTI = 0; +static int ARG_QUALITY = 6; +static int ARG_MINQUANT = 1; +static int ARG_MAXQUANT = 31; +static float ARG_FRAMERATE = 25.00f; +static int ARG_MAXFRAMENR = ABS_MAXFRAMENR; +static char *ARG_INPUTFILE = NULL; +static int ARG_INPUTTYPE = 0; +static int ARG_SAVEDECOUTPUT = 0; +static int ARG_SAVEMPEGSTREAM = 0; +static int ARG_HINTMODE = HINT_MODE_NONE; +static int XDIM = 0; +static int YDIM = 0; +#define IMAGE_SIZE(x,y) ((x)*(y)*3/2) -#define ABS_MAXFRAMENR 9999 // max number of frames +#define MAX(A,B) ( ((A)>(B)) ? (A) : (B) ) +#define SMALL_EPS 1e-10 -int ARG_BITRATE=900; -int ARG_QUANTI=0; +#define LONG_PACK(a,b,c,d) ((long) (((long)(a))<<24) | (((long)(b))<<16) | \ + (((long)(c))<<8) |((long)(d))) -int ARG_QUALITY =6; -int ARG_MINQUANT=1; -int ARG_MAXQUANT=31; -float ARG_FRAMERATE=25.00; +#define SWAP(a) ( (((a)&0x000000ff)<<24) | (((a)&0x0000ff00)<<8) | \ + (((a)&0x00ff0000)>>8) | (((a)&0xff000000)>>24) ) -int ARG_MAXFRAMENR=ABS_MAXFRAMENR; +/**************************************************************************** + * Nasty global vars ;-) + ***************************************************************************/ +static int i,filenr = 0; +static int save_ref_flag = 0; + +/* the path where to save output */ +static char filepath[256] = "./"; + +/* Internal structures (handles) for encoding and decoding */ +static void *enc_handle = NULL; +static void *dec_handle = NULL; + +/***************************************************************************** + * Main program + ****************************************************************************/ + +int main(int argc, char *argv[]) +{ + + unsigned char *divx_buffer = NULL; + unsigned char *in_buffer = NULL; + unsigned char *out_buffer = NULL; + unsigned char *hints_buffer = NULL; + + double enctime,dectime; + double totalenctime=0.; + double totaldectime=0.; + + long totalsize=0; + long hints_size; + int status; + int bigendian; + + long m4v_size; + long frame_type[ABS_MAXFRAMENR]; + int Iframes=0, Pframes=0, use_assembler=0; + double framepsnr[ABS_MAXFRAMENR]; + + double Ipsnr=0.,Imaxpsnr=0.,Iminpsnr=999.,Ivarpsnr=0.; + double Ppsnr=0.,Pmaxpsnr=0.,Pminpsnr=999.,Pvarpsnr=0.; + + char filename[256]; + + FILE *filehandle; + FILE *in_file = stdin; + FILE *hints_file = NULL; + + printf("xvid_stat - XviD core library test program "); + printf("written by Christoph Lampert 2002\n\n"); + +/***************************************************************************** + * Command line parsing + ****************************************************************************/ + + for (i=1; i< argc; i++) { + + if (strcmp("-asm", argv[i]) == 0 ) { + use_assembler = 1; + } + else if (strcmp("-w", argv[i]) == 0 && i < argc - 1 ) { + i++; + XDIM = atoi(argv[i]); + } + else if (strcmp("-h", argv[i]) == 0 && i < argc - 1 ) { + i++; + YDIM = atoi(argv[i]); + } + else if (strcmp("-b", argv[i]) == 0 && i < argc - 1 ) { + i++; + ARG_BITRATE = atoi(argv[i]); + } + else if (strcmp("-q", argv[i]) == 0 && i < argc - 1 ) { + i++; + ARG_QUALITY = atoi(argv[i]); + } + else if (strcmp("-f", argv[i]) == 0 && i < argc - 1 ) { + i++; + ARG_FRAMERATE = (float)atof(argv[i]); + } + else if (strcmp("-i", argv[i]) == 0 && i < argc - 1 ) { + i++; + ARG_INPUTFILE = argv[i]; + } + else if (strcmp("-t", argv[i]) == 0 && i < argc - 1 ) { + i++; + ARG_INPUTTYPE = atoi(argv[i]); + } + else if(strcmp("-n", argv[i]) == 0 && i < argc - 1 ) { + i++; + ARG_MAXFRAMENR = atoi(argv[i]); + } + else if (strcmp("-quant", argv[i]) == 0 && i < argc - 1 ) { + i++; + ARG_QUANTI = atoi(argv[i]); + } + else if (strcmp("-d", argv[i]) == 0 && i < argc - 1 ) { + i++; + ARG_SAVEDECOUTPUT = atoi(argv[i]); + } + else if (strcmp("-m", argv[i]) == 0 && i < argc - 1 ) { + i++; + ARG_SAVEMPEGSTREAM = atoi(argv[i]); + } + else if (strcmp("-mv", argv[i]) == 0 && i < argc - 1 ) { + i++; + ARG_HINTMODE = atoi(argv[i]); + } + else if (strcmp("-help", argv[i])) { + usage(); + return(0); + } + else { + usage(); + exit(-1); + } + + } + +/***************************************************************************** + * Arguments checking + ****************************************************************************/ + + if (XDIM <= 0 || XDIM >= 2048 || YDIM <=0 || YDIM >= 2048 ) { + fprintf(stderr, "Trying to retreive width and height from PGM header\n"); + ARG_INPUTTYPE = 1; /* pgm */ + } + + if ( ARG_QUALITY < 0 || ARG_QUALITY > 6) { + fprintf(stderr,"Wrong Quality\n"); + return -1; + } + + if ( ARG_BITRATE <= 0 && ARG_QUANTI == 0) { + fprintf(stderr,"Wrong Bitrate\n"); + return -1; + } + + if ( ARG_FRAMERATE <= 0) { + fprintf(stderr,"Wrong Framerate %s \n",argv[5]); + return -1; + } + + if ( ARG_MAXFRAMENR <= 0) { + fprintf(stderr,"Wrong number of frames\n"); + return -1; + } + + if ( ARG_HINTMODE != HINT_MODE_NONE && + ARG_HINTMODE != HINT_MODE_GET && + ARG_HINTMODE != HINT_MODE_SET) + ARG_HINTMODE = HINT_MODE_NONE; + + if( ARG_HINTMODE != HINT_MODE_NONE) { + char *rights = "rb"; + + /* + * If we are getting hints from core, we will have to write them to + * hint file + */ + if(ARG_HINTMODE == HINT_MODE_GET) + rights = "w+b"; + + /* Open the hint file */ + hints_file = fopen(HINT_FILE, rights); + if(hints_file == NULL) { + fprintf(stderr, "Error opening input file %s\n", HINT_FILE); + return -1; + } + + /* Allocate hint memory space, we will be using rawhints */ + /* NB : Hope 1Mb is enough */ + if((hints_buffer = malloc(1024*1024)) == NULL) { + fprintf(stderr, "Memory allocation error\n"); + return -1; + } + + } + + if ( ARG_INPUTFILE == NULL || strcmp(ARG_INPUTFILE, "stdin") == 0) { + in_file = stdin; + } + else { + + in_file = fopen(ARG_INPUTFILE, "rb"); + if (in_file == NULL) { + fprintf(stderr, "Error opening input file %s\n", ARG_INPUTFILE); + return -1; + } + } + + if (ARG_INPUTTYPE) { + if (read_pgmheader(in_file)) { + fprintf(stderr, "Wrong input format, I want YUV encapsulated in PGM\n"); + return -1; + } + } + + /* now we know the sizes, so allocate memory */ + + in_buffer = (unsigned char *) malloc(IMAGE_SIZE(XDIM,YDIM)); + if (!in_buffer) + goto free_all_memory; + + /* this should really be enough memory ! */ + divx_buffer = (unsigned char *) malloc(IMAGE_SIZE(XDIM,YDIM)*2); + if (!divx_buffer) + goto free_all_memory; + + out_buffer = (unsigned char *) malloc(IMAGE_SIZE(XDIM,YDIM)*4); + if (!out_buffer) + goto free_all_memory; + + +/***************************************************************************** + * XviD PART Start + ****************************************************************************/ + + + status = enc_init(use_assembler); + if (status) + { + fprintf(stderr, "Encore INIT problem, return value %d\n", status); + goto release_all; + } + + status = dec_init(use_assembler); + if (status) + { + fprintf(stderr, "Decore INIT problem, return value %d\n", status); + goto release_all; + } + + totalsize = LONG_PACK('M','P','4','U'); + if(*((char *)(&totalsize)) == 'M') + bigendian = 1; + else + bigendian = 0; + +/***************************************************************************** + * Main loop + ****************************************************************************/ + + do { + + if (ARG_INPUTTYPE) + status = read_pgmdata(in_file, in_buffer); /* read PGM data (YUV-format) */ + else + status = read_yuvdata(in_file, in_buffer); /* read raw data (YUV-format) */ + + if (status) + { + /* Couldn't read image, most likely end-of-file */ + continue; + } + + + if (save_ref_flag) + { + sprintf(filename, "%s%05d.pgm", filepath, filenr); + write_pgm(filename,in_buffer); + } -#define MAX(A,B) ( ((A)>(B)) ? (A) : (B) ) -#define SMALL_EPS 1e-10 +/***************************************************************************** + * Analyse this frame before encoding + ****************************************************************************/ + +/* + * nothing is done here at the moment, but you could e.g. create + * histograms or measure entropy or apply preprocessing filters... + */ + +/***************************************************************************** + * Read hints from file + ****************************************************************************/ + + if(ARG_HINTMODE == HINT_MODE_SET) { + fread(&hints_size, 1, sizeof(long), hints_file); + hints_size = (!bigendian)?SWAP(hints_size):hints_size; + fread(hints_buffer, 1, hints_size, hints_file); + } + +/***************************************************************************** + * Encode and decode this frame + ****************************************************************************/ + + enctime = msecond(); + status = enc_main(in_buffer, divx_buffer, hints_buffer, + &m4v_size, &frame_type[filenr], &hints_size); + enctime = msecond() - enctime; + + totalenctime += enctime; + totalsize += m4v_size; + + printf("Frame %5d: intra %1d, enctime=%6.1f ms, size=%6d bytes ", + (int)filenr, (int)frame_type[filenr], (float)enctime, (int)m4v_size); + +/***************************************************************************** + * Save hints to file + ****************************************************************************/ + + if(ARG_HINTMODE == HINT_MODE_GET) { + hints_size = (!bigendian)?SWAP(hints_size):hints_size; + fwrite(&hints_size, 1, sizeof(long), hints_file); + hints_size = (!bigendian)?SWAP(hints_size):hints_size; + fwrite(hints_buffer, 1, hints_size, hints_file); + } + +/***************************************************************************** + * Save stream to file + ****************************************************************************/ + + if (ARG_SAVEMPEGSTREAM) + { + sprintf(filename, "%sframe%05d.m4v", filepath, filenr); + filehandle = fopen(filename, "wb"); + fwrite(divx_buffer, m4v_size, 1, filehandle); + fclose(filehandle); + } -/* these are global variables. Not very elegant, but easy, and this is an easy program */ + dectime = msecond(); + status = dec_main(divx_buffer, out_buffer, m4v_size); + dectime = msecond() - dectime; + + totaldectime += dectime; + + +/***************************************************************************** + * Analyse the decoded frame and compare to original + ****************************************************************************/ + + framepsnr[filenr] = PSNR(XDIM,YDIM*3/2, in_buffer, XDIM, out_buffer, XDIM); + + printf("dectime =%6.1f ms PSNR %5.2f\n",dectime, framepsnr[filenr]); + + if (ARG_SAVEDECOUTPUT) + { + sprintf(filename, "%sdec%05d.pgm", filepath, filenr); + write_pgm(filename, out_buffer); + } + + /* Read the header if it's pgm stream */ + if (ARG_INPUTTYPE) + status = read_pgmheader(in_file); + + filenr++; + + } while ( (!status) && (filenr Pmaxpsnr) + Pmaxpsnr = framepsnr[i]; + if (framepsnr[i] < Pminpsnr) + Pminpsnr = framepsnr[i]; + Pvarpsnr += (framepsnr[i] - Ppsnr)*(framepsnr[i] - Ppsnr) /Pframes; + break; + case 1: + if (framepsnr[i] > Imaxpsnr) + Imaxpsnr = framepsnr[i]; + if (framepsnr[i] < Pminpsnr) + Iminpsnr = framepsnr[i]; + Ivarpsnr += (framepsnr[i] - Ipsnr)*(framepsnr[i] - Ipsnr) /Iframes; + default: + break; + } + } + + /* Print all statistics */ + printf("Avg. Q%1d %2s ",ARG_QUALITY, (ARG_QUANTI ? " q" : "br")); + printf("%04d ",(ARG_QUANTI)?ARG_QUANTI:ARG_BITRATE); + printf("( %.2f bpp) ", (double)ARG_BITRATE*1000/XDIM/YDIM/ARG_FRAMERATE); + printf("size %6d ", (int)totalsize); + printf("( %4d kbps ",(int)(totalsize*8*ARG_FRAMERATE/1000)); + printf("/ %.2f bpp) ",(double)totalsize*8/XDIM/YDIM); + printf("enc: %6.1f fps, dec: %6.1f fps \n",1000/totalenctime, 1000/totaldectime); + printf("PSNR P(%d): %5.2f ( %5.2f , %5.2f ; %5.4f ) ",Pframes,Ppsnr,Pminpsnr,Pmaxpsnr,sqrt(Pvarpsnr/filenr)); + printf("I(%d): %5.2f ( %5.2f , %5.2f ; %5.4f ) ",Iframes,Ipsnr,Iminpsnr,Imaxpsnr,sqrt(Ivarpsnr/filenr)); + printf("\n"); + +/***************************************************************************** + * XviD PART Stop + ****************************************************************************/ + + release_all: + + if (enc_handle) + { + status = enc_stop(); + if (status) + fprintf(stderr, "Encore RELEASE problem return value %d\n", status); + } + + if (dec_handle) + { + status = dec_stop(); + if (status) + fprintf(stderr, "Decore RELEASE problem return value %d\n", status); + } + + fclose(in_file); + + free_all_memory: + free(out_buffer); + free(divx_buffer); + free(in_buffer); + + return 0; + +} + +/***************************************************************************** + * "statistical" functions + * + * these are not needed for encoding or decoding, but for measuring + * time and quality, there in nothing specific to XviD in these + * + *****************************************************************************/ + + + +/* Return time elapsed time in miliseconds since the program started */ +static double msecond() { +#ifndef _MSC_VER struct timeval tv; gettimeofday(&tv, 0); - return tv.tv_sec + tv.tv_usec * 1.0e-6; + return tv.tv_sec*1.0e3 + tv.tv_usec * 1.0e-3; +#else + clock_t clk; + clk = clock(); + return clk * 1000 / CLOCKS_PER_SEC; +#endif } - -double absdistq(int x,int y, unsigned char* buf1, int stride1, unsigned char* buf2, int stride2) -/* returns the sum of squared distances (SSD) between two images of dimensions x times y */ +/* + * Returns the sum of squared distances (SSD) between two images of dimensions + * x times y + */ +static double absdistq(int x, int y, + unsigned char* buf1, int stride1, + unsigned char* buf2, int stride2) { double dist=0.; int i,j,val; @@ -159,28 +669,68 @@ buf1 += stride1; buf2 += stride2; } - return dist/(x*y); + return dist/(x*y); } -double PSNR(int x,int y, unsigned char* buf1, int stride1, unsigned char* buf2, int stride2 ) -/* return the PSNR between to images */ -/* this is a logarithmic measure for "quality" from the world of signal processing */ -/* if you don't know what it is, simply accept that higher values are better */ +/* + * Returns the PSNR between to images. + * + * This is a common logarithmic measure for "quality" from the world of signal + * processing if you don't know what it is, simply accept that higher values + * are better. + * + * PSNR represents the ratio of useful signal over noise signal. In our case, + * useful signal is refernce image, noise signal is the difference between + * reference and decoded frame from encoded bitstream. + * + * The problem is this type of value is dependant of image source and so, is + * not reliable as a common "quality" indicator. + * So PSNR computes the ratio of maximum/noise. Maximum being set to 2^bpp/channel + * This way, PSNR is not dependant anymore of image data type. + * + */ +static double PSNR(int x, int y, + unsigned char* buf1, int stride1, + unsigned char* buf2, int stride2) { - return 10*(log10(255*255)-log10( absdistq(x, y, buf1, stride1, buf2, stride2) )); + return 10*(log10(255*255)-log10( absdistq(x, y, buf1, stride1, buf2, stride2) )); } +/***************************************************************************** + * Usage message + *****************************************************************************/ + +static void usage() +{ + + fprintf(stderr, "Usage : xvid_stat [OPTIONS]\n"); + fprintf(stderr, "Options :\n"); + fprintf(stderr, " -w integer : frame width ([1.2048])\n"); + fprintf(stderr, " -h integer : frame height ([1.2048])\n"); + fprintf(stderr, " -b integer : target bitrate (>0 | default=900kbit)\n"); + fprintf(stderr, " -f float : target framerate (>0)\n"); + fprintf(stderr, " -i string : input filename (default=stdin)\n"); + fprintf(stderr, " -t integer : input data type (yuv=0, pgm=1)\n"); + fprintf(stderr, " -n integer : number of frames to encode\n"); + fprintf(stderr, " -q integer : quality ([0..5])\n"); + fprintf(stderr, " -d boolean : save decoder output (0 False*, !=0 True)\n"); + fprintf(stderr, " -m boolean : save mpeg4 raw stream (0 False*, !=0 True)\n"); + fprintf(stderr, " -help : prints this help message\n"); + fprintf(stderr, " -quant integer : fixed quantizer (disables -b setting)\n"); + fprintf(stderr, " (* means default)\n"); -/*********************************************************************/ -/* input and output functions */ -/* */ -/* the are small and simple routines to read and write PGM and YUV */ -/* image. It's just for convenience, again nothing specific to XviD */ -/* */ -/*********************************************************************/ +} + +/***************************************************************************** + * Input and output functions + * + * the are small and simple routines to read and write PGM and YUV + * image. It's just for convenience, again nothing specific to XviD + * + *****************************************************************************/ -int read_pgmheader(FILE* handle) +static int read_pgmheader(FILE* handle) { int bytes,xsize,ysize,depth; char dummy[2]; @@ -189,6 +739,7 @@ if ( (bytes < 2) || (dummy[0] != 'P') || (dummy[1] != '5' )) return 1; + fscanf(handle,"%d %d %d",&xsize,&ysize,&depth); if ( (xsize > 1440) || (ysize > 2880 ) || (depth != 255) ) { @@ -196,89 +747,120 @@ return 2; } if ( (XDIM==0) || (YDIM==0) ) - { XDIM=xsize; - YDIM=ysize; + { + XDIM=xsize; + YDIM=ysize*2/3; } return 0; } -int read_pgmdata(FILE* handle, unsigned char *image) +static int read_pgmdata(FILE* handle, unsigned char *image) { - int i,status; + int i; char dummy; - - unsigned char* buff1_ptr2 = image + XDIM*YDIM; - unsigned char* buff1_ptr3 = image + XDIM*YDIM + XDIM/2*YDIM/2; + + unsigned char *y = image; + unsigned char *u = image + XDIM*YDIM; + unsigned char *v = image + XDIM*YDIM + XDIM/2*YDIM/2; - fread(image,XDIM*YDIM,1,stdin); // read Y component of picture + /* read Y component of picture */ + fread(y, 1, XDIM*YDIM, handle); - for (i=0;i + xframe.colorspace = XVID_CSP_YV12; /* defined in */ - xframe.intra = -1; // let the codec decide between I-frame (1) and P-frame (0) + xframe.intra = -1; /* let the codec decide between I-frame (1) and P-frame (0) */ - xframe.quant = ARG_QUANTI; // is quant != 0, use a fixed quant (and ignore bitrate) + xframe.quant = ARG_QUANTI; /* is quant != 0, use a fixed quant (and ignore bitrate) */ xframe.motion = motion_presets[ARG_QUALITY]; xframe.general = general_presets[ARG_QUALITY]; xframe.quant_intra_matrix = xframe.quant_inter_matrix = NULL; + xframe.hint.hintstream = hints_buffer; + + if(ARG_HINTMODE == HINT_MODE_SET) { + xframe.hint.hintlength = *hints_size; + xframe.hint.rawhints = 0; + xframe.general |= XVID_HINTEDME_SET; + } + + if(ARG_HINTMODE == HINT_MODE_GET) { + xframe.hint.rawhints = 0; + xframe.general |= XVID_HINTEDME_GET; + } + xerr = xvid_encore(enc_handle, XVID_ENC_ENCODE, &xframe, &xstats); -/* enc_result->is_key_frame = xframe.intra; - enc_result->quantizer = xframe.quant; - enc_result->total_bits = xframe.length * 8; - enc_result->motion_bits = xstats.hlength * 8; - enc_result->texture_bits = enc_result->total_bits - enc_result->motion_bits; -*/ - -/* This is statictical data, e.g. for 2-pass. - If you are not interested in any of this, you can use NULL instead of &xstats -*/ + if(ARG_HINTMODE == HINT_MODE_GET) + *hints_size = xframe.hint.hintlength; + + /* + * This is statictical data, e.g. for 2-pass. If you are not + * interested in any of this, you can use NULL instead of &xstats + */ *frametype = xframe.intra; *streamlength = xframe.length; return xerr; } +/***************************************************************************** + * Routines for decoding: init encoder, frame step, release encoder + ****************************************************************************/ -/*********************************************************************/ -/* Routines for decoding: init encoder, frame step, release encoder */ -/*********************************************************************/ - -int dec_init(int use_assembler) /* init decoder before first run */ +/* init decoder before first run */ +static int dec_init(int use_assembler) { - int xerr; + int xerr; - XVID_INIT_PARAM xinit; - XVID_DEC_PARAM xparam; + XVID_INIT_PARAM xinit; + XVID_DEC_PARAM xparam; - if(use_assembler) + if(use_assembler) #ifdef ARCH_IA64 - xinit.cpu_flags = XVID_CPU_FORCE | XVID_CPU_IA64; + xinit.cpu_flags = XVID_CPU_FORCE | XVID_CPU_IA64; #else - xinit.cpu_flags = 0; + xinit.cpu_flags = 0; #endif - else - xinit.cpu_flags = XVID_CPU_FORCE; + else + xinit.cpu_flags = XVID_CPU_FORCE; - xvid_init(NULL, 0, &xinit, NULL); - xparam.width = XDIM; - xparam.height = YDIM; + xvid_init(NULL, 0, &xinit, NULL); + xparam.width = XDIM; + xparam.height = YDIM; - xerr = xvid_decore(NULL, XVID_DEC_CREATE, &xparam, NULL); - dec_handle = xparam.handle; + xerr = xvid_decore(NULL, XVID_DEC_CREATE, &xparam, NULL); + dec_handle = xparam.handle; - return xerr; + return xerr; } -int dec_main(unsigned char *m4v_buffer, unsigned char *out_buffer, int m4v_size) -{ /* decode one frame */ - - int xerr; - XVID_DEC_FRAME xframe; +/* decode one frame */ +static int dec_main(unsigned char *m4v_buffer, unsigned char *out_buffer, + int m4v_size) +{ + int xerr; + XVID_DEC_FRAME xframe; - xframe.bitstream = m4v_buffer; - xframe.length = m4v_size; - xframe.image = out_buffer; - xframe.stride = XDIM; - xframe.colorspace = XVID_CSP_YV12; // XVID_CSP_USER is fastest (no memcopy involved) + xframe.bitstream = m4v_buffer; + xframe.length = m4v_size; + xframe.image = out_buffer; + xframe.stride = XDIM; + xframe.colorspace = XVID_CSP_YV12; /* XVID_CSP_USER is fastest (no memcopy involved) */ - xerr = xvid_decore(dec_handle, XVID_DEC_DECODE, &xframe, NULL); + xerr = xvid_decore(dec_handle, XVID_DEC_DECODE, &xframe, NULL); - return xerr; + return xerr; } -int dec_stop() /* close decoder to release resources */ +/* close decoder to release resources */ +static int dec_stop() { - int xerr; - xerr = xvid_decore(dec_handle, XVID_DEC_DESTROY, NULL, NULL); + int xerr; + xerr = xvid_decore(dec_handle, XVID_DEC_DESTROY, NULL, NULL); - return xerr; + return xerr; } - -/*********************************************************************/ -/* Main program */ -/*********************************************************************/ - -int main(int argc, char *argv[]) -{ - unsigned char *divx_buffer = NULL; - unsigned char *in_buffer = NULL; - unsigned char *out_buffer = NULL; - - double enctime,dectime; - double totalenctime=0.; - double totaldectime=0.; - - long totalsize=0; - int status; - - int m4v_size; - int frame_type[ABS_MAXFRAMENR]; - int Iframes=0, Pframes=0, Bframes=0, use_assembler=0; - double framepsnr[ABS_MAXFRAMENR]; - - double Ipsnr=0.,Imaxpsnr=0.,Iminpsnr=999.,Ivarpsnr=0.; - double Ppsnr=0.,Pmaxpsnr=0.,Pminpsnr=999.,Pvarpsnr=0.; - double Bpsnr=0.,Bmaxpsnr=0.,Bminpsnr=999.,Bvarpsnr=0.; - - char filename[256]; - - FILE *filehandle; - -/* read YUV in pgm format from stdin */ - if (!pgmflag) - { - pgmflag = 1; - - if (argc==2 && !strcmp(argv[1],"-asm")) - use_assembler = 1; - if (argc>=3) - { XDIM = atoi(argv[1]); - YDIM = atoi(argv[2]); - if ( (XDIM <= 0) || (XDIM >= 2048) || (YDIM <=0) || (YDIM >= 2048) ) - { fprintf(stderr,"Wrong frames size %d %d, trying PGM \n",XDIM, YDIM); - } - else - { - YDIM = YDIM*3/2; /* for YUV */ - pgmflag = 0; - } - } - } - - if (pgmflag) - { if (read_pgmheader(stdin)) - { - printf("Wrong input format, I want YUV encapsulated in PGM\n"); - return 1; - } - } - if (argc>=4) - { ARG_QUALITY = atoi(argv[3]); - if ( (ARG_QUALITY < 0) || (ARG_QUALITY > 6) ) - { fprintf(stderr,"Wrong Quality\n"); return -1; } - else - printf("Quality %d\n",ARG_QUALITY); - } - if (argc>=5) - { ARG_BITRATE = atoi(argv[4]); - if ( (ARG_BITRATE <= 0) ) - { fprintf(stderr,"Wrong Bitrate\n"); return -1; } - if ( (ARG_BITRATE <= 32) ) - { ARG_QUANTI = ARG_BITRATE; - ARG_BITRATE=0; - printf("Quantizer %d\n",ARG_QUANTI); - } - else - printf("Bitrate %d kbps\n",ARG_BITRATE); - } - if (argc>=6) - { ARG_FRAMERATE = (float)atof(argv[5]); - if ( (ARG_FRAMERATE <= 0) ) - { fprintf(stderr,"Wrong Fraterate %s \n",argv[5]); return -1; } - printf("Framerate %6.3f fps\n",ARG_FRAMERATE); - } - - if (argc>=7) - { ARG_MAXFRAMENR = atoi(argv[6]); - if ( (ARG_MAXFRAMENR <= 0) ) - { fprintf(stderr,"Wrong number of frames\n"); return -1; } - printf("max. Framenr. %d\n",ARG_MAXFRAMENR); - } - -/* now we know the sizes, so allocate memory */ - - in_buffer = (unsigned char *) malloc(XDIM*YDIM); - if (!in_buffer) - goto free_all_memory; // goto is one of the most underestimated instructions in C !!! - - divx_buffer = (unsigned char *) malloc(XDIM*YDIM*2); // this should really be enough memory! - if (!divx_buffer) - goto free_all_memory; - - YDIM = YDIM*2/3; // PGM is YUV 4:2:0 format, so real image height is *2/3 of PGM picture - - out_buffer = (unsigned char *) malloc(XDIM*YDIM*4); - if (!out_buffer) - goto free_all_memory; - - -/*********************************************************************/ -/* XviD PART Start */ -/*********************************************************************/ - - - status = enc_init(use_assembler); - if (status) - { - printf("Encore INIT problem, return value %d\n", status); - goto release_all; - } - - status = dec_init(use_assembler); - if (status) - { - printf("Decore INIT problem, return value %d\n", status); - goto release_all; - } - - -/*********************************************************************/ -/* Main loop */ -/*********************************************************************/ - - do - { - if (pgmflag) - status = read_pgmdata(stdin, in_buffer); // read PGM data (YUV-format) - else - status = read_yuvdata(stdin, in_buffer); // read raw data (YUV-format) - - if (status) - { - // Couldn't read image, most likely end-of-file - continue; - } - - - if (save_ref_flag) - { - sprintf(filename, "%s%05d.pgm", filepath, filenr); - write_pgm(filename,in_buffer); - } - - -/*********************************************************************/ -/* analyse this frame before encoding */ -/*********************************************************************/ - -// nothing is done here at the moment, but you could e.g. create -// histograms or measure entropy or apply preprocessing filters... - -/*********************************************************************/ -/* encode and decode this frame */ -/*********************************************************************/ - - enctime = -msecond(); - status = enc_main(in_buffer, divx_buffer, &m4v_size, &frame_type[filenr]); - enctime += msecond(); - - totalenctime += enctime; - totalsize += m4v_size; - - printf("Frame %5d: intra %d, enctime =%6.1f ms length=%7d bytes ", - filenr, frame_type[filenr], enctime*1000, m4v_size); - - if (save_m4v_flag) - { - sprintf(filename, "%sframe%05d.m4v", filepath, filenr); - filehandle = fopen(filename, "wb"); - fwrite(divx_buffer, m4v_size, 1, filehandle); - fclose(filehandle); - } - - dectime = -msecond(); - status = dec_main(divx_buffer, out_buffer, m4v_size); - dectime += msecond(); - - totaldectime += dectime; - - -/*********************************************************************/ -/* analyse the decoded frame and compare to original */ -/*********************************************************************/ - - framepsnr[filenr] = PSNR(XDIM,YDIM, in_buffer, XDIM, out_buffer, XDIM ); - - printf("dectime =%6.1f ms PSNR %5.2f\n",dectime*1000, framepsnr[filenr]); - - if (save_dec_flag) - { - sprintf(filename, "%sdec%05d.pgm", filepath, filenr); - write_pgm(filename,out_buffer); - } - - if (pgmflag) - status = read_pgmheader(stdin); // because if this was the last PGM, stop now - - filenr++; - - } while ( (!status) && (filenr Pmaxpsnr) - Pmaxpsnr = framepsnr[i]; - if (framepsnr[i] < Pminpsnr) - Pminpsnr = framepsnr[i]; - Pvarpsnr += (framepsnr[i] - Ppsnr)*(framepsnr[i] - Ppsnr) /Pframes; - break; - case 1: - if (framepsnr[i] > Imaxpsnr) - Imaxpsnr = framepsnr[i]; - if (framepsnr[i] < Pminpsnr) - Iminpsnr = framepsnr[i]; - Ivarpsnr += (framepsnr[i] - Ipsnr)*(framepsnr[i] - Ipsnr) /Iframes; - break; - case 2: - if (framepsnr[i] > Bmaxpsnr) - Bmaxpsnr = framepsnr[i]; - if (framepsnr[i] < Pminpsnr) - Bminpsnr = framepsnr[i]; - Bvarpsnr += (framepsnr[i] - Bpsnr)*(framepsnr[i] - Bpsnr) /Bframes; - break; - } - } - - printf("Avg. Q%1d %2s ",ARG_QUALITY, (ARG_QUANTI ? " q" : "br")); - printf("%04d ",MAX(ARG_QUANTI,ARG_BITRATE)); - printf("( %.2f bpp) ", (double)ARG_BITRATE*1000/XDIM/YDIM/ARG_FRAMERATE); - printf("size %6d ",totalsize); - printf("( %4d kbps ",(int)(totalsize*8*ARG_FRAMERATE/1000)); - printf("/ %.2f bpp) ",(double)totalsize*8/XDIM/YDIM); - printf("enc: %6.1f fps, dec: %6.1f fps \n",1/totalenctime, 1/totaldectime); - printf("PSNR P(%d): %5.2f ( %5.2f , %5.2f ; %5.4f ) ",Pframes,Ppsnr,Pminpsnr,Pmaxpsnr,sqrt(Pvarpsnr/filenr)); - printf("I(%d): %5.2f ( %5.2f , %5.2f ; %5.4f ) ",Iframes,Ipsnr,Iminpsnr,Imaxpsnr,sqrt(Ivarpsnr/filenr)); - if (Bframes) - printf("B(%d): %5.2f ( %5.2f , %5.2f ; %5.4f ) ",Bframes,Bpsnr,Bminpsnr,Bmaxpsnr,sqrt(Bvarpsnr/filenr)); - printf("\n"); - -/*********************************************************************/ -/* XviD PART Stop */ -/*********************************************************************/ - -release_all: - - if (enc_handle) - { - status = enc_stop(); - if (status) - printf("Encore RELEASE problem return value %d\n", status); - } - - if (dec_handle) - { - status = dec_stop(); - if (status) - printf("Decore RELEASE problem return value %d\n", status); - } - - -free_all_memory: - free(out_buffer); - free(divx_buffer); - free(in_buffer); - - return 0; -} +/* EOF */