/************************************************************************** * * 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 #include // needed for log10 #include // only needed for gettimeofday #include "xvid.h" /* comes with XviD */ int motion_presets[7] = { 0, // Q 0 0, // Q 1 0, // Q 2 PMV_HALFPELREFINE16, // Q 3 PMV_HALFPELREFINE16, // Q 4 PMV_HALFPELREFINE16 | PMV_HALFPELREFINE8, // Q 5 PMV_HALFPELREFINE16 | PMV_EXTSEARCH16 // Q 6 | PMV_USESQUARES8 | PMV_USESQUARES16 | 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_I420; // 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