--- xvid_bstat.c 2002/09/29 16:38:06 1.1 +++ xvid_bstat.c 2002/09/29 16:38:06 1.1.2.1 @@ -0,0 +1,841 @@ +/************************************************************************** + * + * XVID MPEG-4 VIDEO CODEC - Example for encoding and decoding + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + *************************************************************************/ + +/************************************************************************ + * + * PSNR and Speed test routine for XviD using the XviD-API + * (C) Christoph Lampert, 2002/04/13 + * + * This is programs isn't finished... it's just a quick hack because people + * asked for a version of xvid_bstat with bframe-support + * + * 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 + * + * gcc xvid_bstat.c -lxvidcore -lm -o xvid_bstat + * + * Run without or with illegal parameters, then PGM input input is read + * from stdin. + * + * Parameters are: xvid_bstat XDIM YDIM QUALITY BITRATE/QUANTIZER FRAMERATE + * MAX_FRAMES MAX_BFRAMES BFRAME_QUANT + * + * 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) + * + * 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 + * + * MAX_FRAMEs is the total number of frames to be encoded (minimum 1) + * + * MAX_BFRAMES is the maximum number of bframes in a row (or -1) + * + * BFRAME_QUANT is the quantization ratio for B-frames (in percent) + * + * 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_bstat.examples + * + ************************************************************************/ + +#define BFRAMES + +#include +#include +#include // needed for log10 +#include // only needed for gettimeofday + +#include "xvid.h" /* comes with XviD */ + +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_USESQUARES8 | PMV_USESQUARES16 | PMV_EARLYSTOP8 | PMV_HALFPELREFINE8 + }; + +int general_presets[7] = { + XVID_H263QUANT, /* or use XVID_MPEGQUANT */ // Q 0 + XVID_H263QUANT, // Q 1 + XVID_H263QUANT | XVID_HALFPEL, // 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 */ + +#define ABS_MAXFRAMENR 9999 // max number of frames + +int ARG_BITRATE=800; +int ARG_QUANTI=0; + +int ARG_QUALITY =6; +int ARG_MINQUANT=1; +int ARG_MAXQUANT=31; +float ARG_FRAMERATE=25.00; + +int ARG_MAX_BFRAMES=2; +int ARG_BQUANT_RATIO=200; +int NUMBUFFERS; + +int ARG_MAXFRAMENR=ABS_MAXFRAMENR; + + +#define MAX(A,B) ( ((A)>(B)) ? (A) : (B) ) +#define SMALL_EPS 1e-10 + +/* these are global variables. Not very elegant, but easy, and this is an easy program */ + +int XDIM=0; +int YDIM=0; // will be set when reading first image +int i,filenr = 0; + +int save_m4v_flag = 0; // save MPEG4-bytestream? +int save_dec_flag = 1; // save decompressed bytestream? +int save_ref_flag = 0; // + +int pgmflag = 0; // a flag, if input is in PGM format, overwritten in init-phase +char filepath[256] = "./"; // the path where to save output + +void *enc_handle = NULL; // internal structures (handles) for encoding +void *dec_handle = NULL; // and decoding + + +/*********************************************************************/ +/* "statistical" functions */ +/* */ +/* these are not needed for encoding or decoding, but for measuring */ +/* time and quality, there in nothing specific to XviD in these */ +/* */ +/*********************************************************************/ + +double msecond() +/* return the current time in seconds(!) */ +{ + struct timeval tv; + gettimeofday(&tv, 0); + return tv.tv_sec + tv.tv_usec * 1.0e-6; +} + + + +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 */ +{ + double dist=0.; + int i,j,val; + + for (i=0;i 1440) || (ysize > 2880 ) || (depth != 255) ) + { + fprintf(stderr,"%d %d %d\n",xsize,ysize,depth); + return 2; + } + if ( (XDIM==0) || (YDIM==0) ) + { XDIM=xsize; + YDIM=ysize; + } + + return 0; +} + +int read_pgmdata(FILE* handle, unsigned char *image) +{ + int i,status; + char dummy; + + unsigned char* buff1_ptr2 = image + XDIM*YDIM; + unsigned char* buff1_ptr3 = image + XDIM*YDIM + XDIM/2*YDIM/2; + + fread(image,XDIM*YDIM,1,stdin); // read Y component of picture + + for (i=0;i + + xframe.intra = -1; // let the codec decide between I-frame (1) and P-frame (0) + + xframe.quant = ARG_QUANTI; // if quant != 0, use a fixed quant (and ignore bitrate) + xframe.bquant = 0; + + + xframe.motion = motion_presets[ARG_QUALITY]; + xframe.general = general_presets[ARG_QUALITY]; + xframe.quant_intra_matrix = xframe.quant_inter_matrix = NULL; + + 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 +*/ + *frametype = xframe.intra; + *streamlength = xframe.length; + + return xerr; +} + + +/*********************************************************************/ +/* Routines for decoding: init encoder, frame step, release encoder */ +/*********************************************************************/ + +int dec_init(int use_assembler) /* init decoder before first run */ +{ + int xerr; + + XVID_INIT_PARAM xinit; + XVID_DEC_PARAM xparam; + + if(use_assembler) + +#ifdef ARCH_IA64 + xinit.cpu_flags = XVID_CPU_FORCE | XVID_CPU_IA64; +#else + xinit.cpu_flags = 0; +#endif + + else + xinit.cpu_flags = XVID_CPU_FORCE; + + 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; + + 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; + + xframe.bitstream = m4v_buffer; + xframe.length = 1234; // *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); + + *m4v_size = xframe.length; + return xerr; +} + +int dec_stop() /* close decoder to release resources */ +{ + int xerr; + xerr = xvid_decore(dec_handle, XVID_DEC_DESTROY, NULL, NULL); + + return xerr; +} + + +/*********************************************************************/ +/* Main program */ +/*********************************************************************/ + +int main(int argc, char *argv[]) +{ + unsigned char *divx_buffer = NULL; + unsigned char *divx_read = NULL; + unsigned char *divx_write = NULL; + unsigned char **in_buffer; + unsigned char *out_buffer = NULL; + unsigned char *divx_pos; + + int k; + + double enctime,dectime; + double totalenctime=0.; + double totaldectime=0.; + + long totalsize=0; + int status; + + int loop; + int m4v_size=0; + int enc_size=0; + 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 <= 31) ) + { 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); + } + + if (argc>=8) + { ARG_MAX_BFRAMES = atoi(argv[7]); + if ( (ARG_MAX_BFRAMES < -1) ) + { fprintf(stderr,"Wrong number of bframes\n"); return -1; } + printf("max. B-Frames %d\n",ARG_MAX_BFRAMES); + } + NUMBUFFERS = ARG_MAX_BFRAMES+2; + + if (argc>=9) + { ARG_BQUANT_RATIO = atoi(argv[8]); + if ( (ARG_BQUANT_RATIO <= 0) ) + { fprintf(stderr,"Wrong bquant ratio\n"); return -1; } + printf("bquant ratio %d\n",ARG_BQUANT_RATIO); + } + +/* now we know the sizes, so allocate memory */ + + in_buffer = malloc( NUMBUFFERS*sizeof(unsigned char*) ); + if (!in_buffer) + goto free_all_memory; + + for (k=0;kARG_MAX_BFRAMES) { + framepsnr[filenr-(ARG_MAX_BFRAMES+1)] = PSNR(XDIM,YDIM, in_buffer[(filenr-(ARG_MAX_BFRAMES+1))%NUMBUFFERS], XDIM, out_buffer, XDIM ); + + printf("dectime =%6.1f ms PSNR %5.2f\n",dectime*1000, framepsnr[filenr-(ARG_MAX_BFRAMES+1)]); + } + + if (filenr>ARG_MAX_BFRAMES) + if (save_dec_flag) + { + sprintf(filename, "%sdec%05d.pgm", filepath, filenr-(ARG_MAX_BFRAMES+1)); + write_pgm(filename,out_buffer); + } + +nextfile: + + filenr++; + + if ((int)(divx_read - divx_buffer) > DIVX_SIZE) + { + memmove(divx_buffer, divx_read, (int)(divx_buffer+2*DIVX_SIZE-divx_read)); + divx_write -= (int)(divx_read-divx_buffer); + divx_read = divx_buffer; + fprintf(stderr,"DIVX-buffer copied\n"); + + } + + if (pgmflag) + status = read_pgmheader(stdin); // because if this was the last PGM, stop now + + } 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("mbf %01d ",ARG_MAX_BFRAMES); + printf("bqr %03d ",ARG_MAX_BFRAMES>0 ? ARG_BQUANT_RATIO : 0); + printf("( %.3f bpp) ", (double)ARG_BITRATE*1000/XDIM/YDIM/ARG_FRAMERATE); + printf("size %6d ",totalsize); + printf("( %4d kbps ",(int)(totalsize*8*ARG_FRAMERATE/1000)); + printf("/ %.3f bpp) ",(double)totalsize*8/XDIM/YDIM); + printf("enc: %6.1f fps, dec: %6.1f fps ",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); + if (in_buffer) + for (k=0;k