posts - 34, comments - 90, trackbacks - 0, articles - 0
         最近这几天研究H.263编码,前几天把标准H.263解码库TMNDEC2.0理解了,这两天把H.263的标准编码库研究了一下,编码库的使用明显比解码库要复杂,一眼看上去好你多了很多的代码,令的有点眼花。花了点时间,注释了代码,发现比解码多出来的,主要是进行码率的控制的。而且还使用了两种方法,其它多出来的,就是很多的编码参数(它都弄成全局变量了)。过几天弄一个固定帧率(变码率)的精简版本DEMO出来。
/************************************************************************9
*
*  main.c, main module of tmn (TMN encoder).
*  tmn is an H.263 encoder somewhat based on the Test Model Near-term
*  (TMN5) in the ITU-T LBC Experts Group.
*
*  Copyright (C) 1995, 1996  Telenor R&D, Norway
*        Karl Olav Lillevold <Karl.Lillevold@nta.no>

*  Contacts:
*  Karl Olav Lillevold               <Karl.Lillevold@nta.no>, or
*  Robert Danielsen                  <Robert.Danielsen@nta.no>
*
*  Telenor Research and Development 
http://www.nta.no/brukere/DVC/
*  P.O.Box 83                        tel.:   +47 63 84 84 00
*  N-2007 Kjeller, Norway            fax.:   +47 63 81 00 76

***********************************************************************
*/


/*
* Disclaimer of Warranty
*
* These software programs are available to the user without any
* license fee or royalty on an "as is" basis.  Telenor Research and
* Development disclaims any and all warranties, whether express,
* implied, or statuary, including any implied warranties or
* merchantability or of fitness for a particular purpose.  In no
* event shall the copyright-holder be liable for any incidental,
* punitive, or consequential damages of any kind whatsoever arising
* from the use of these programs.
*
* This disclaimer of warranty extends to the user of these programs
* and user's customers, employees, agents, transferees, successors,
* and assigns.
*
* Telenor Research and Development does not represent or warrant that
* the programs furnished hereunder are free of infringement of any
* third-party patents.
*
* Commercial implementations of H.263, including shareware, are
* subject to royalty fees to patent holders.  Many of these patents
* are general enough such that they are unavoidable regardless of
* implementation design.
*
*/


/* 修改:

    修改人:    Tinnal<www.cnitblog.com/tinnal/>
    目期:    2008-9-1
    改动:    增加中文注释
*/




#include
"sim.h"

FILE
*streamfile;

void main(int argc, char *argv[])
{
    PictImage
*prev_image = NULL;
    PictImage
*curr_image = NULL;
    PictImage
*curr_recon = NULL;
    PictImage
*prev_recon = NULL;

   
/* PB-frame specific */
    PictImage
*B_recon = NULL;
    PictImage
*B_image = NULL;

    Pict
*pic = (Pict *)malloc(sizeof(Pict));
    unsigned
char *image;
    FILE
*cleared;

   
int i;
   
float mean_frame_rate, ref_frame_rate, frame_rate, seconds;
   
int first_loop_finished=0;
   
int total_frames_passed, PPFlag = 0, targetrate;
   
int frames,bframes,pframes,wcopies,icopies, write_repeated,pdist=0,bdist=0;
   
int start, end, frame_no, writediff; 
   
int first_frameskip, chosen_frameskip, orig_frameskip, frameskip;

   
int QP, QPI;
    Bits
*bits = (Bits *)malloc(sizeof(Bits));
    Bits
*total_bits = (Bits *)malloc(sizeof(Bits));
    Bits
*intra_bits = (Bits *)malloc(sizeof(Bits));
    Results
*res = (Results *)malloc(sizeof(Results));
    Results
*total_res = (Results *)malloc(sizeof(Results));
    Results
*b_res = (Results *)malloc(sizeof(Results));
   
char *seqfilename = (char *)malloc(sizeof(char)*100);
   
char *streamname = (char *)malloc(sizeof(char)*100);
   
char *outputfile =  (char *)malloc(sizeof(char)*100);
   
char *diff_filename=DEF_DIFFILENAME;
   
char *tracefile =  (char *)malloc(sizeof(char)*100);

#ifndef OFFLINE_RATE_CONTROL
   
// 码率控制用的变量
    float DelayBetweenFramesInSeconds;
   
int CommBacklog;
#else
    PictImage
*stored_image = NULL;
   
int start_rate_control;
#endif

   
extern int arith_used;

    fprintf (stdout,
"\nTMN (H.263) coder version 2.0, Copyright (C) 1995, 1996 Telenor R&D, Norway\n");

    headerlength
= DEF_HEADERLENGTH;

#ifndef FASTIDCT
    init_idctref();
#endif

   
/* Default variable values */
   
// 是否采用先进预测模式
    advanced = DEF_ADV_MODE;
   
// 是否采用算术编码而不是哈夫曼编码
    syntax_arith_coding = DEF_SAC_MODE;
   
// 是否采用无限制向量长度模式
    pic->unrestricted_mv_mode = DEF_UMV_MODE;
    mv_outside_frame
= DEF_UMV_MODE || DEF_ADV_MODE;
    long_vectors
= DEF_UMV_MODE;
   
// 是否采用PB模式
    pb_frames = DEF_PBF_MODE;

   
// P帧量化矩阵参数
    QP = DEF_INTER_QUANT;
   
// I帧量化矩阵参数
    QPI = DEF_INTRA_QUANT;
   
// B帧量化矩阵参数
    pic->BQUANT = DEF_BQUANT;
   
// 编码类型(one of SF_SQCIF, SF_QCIF, SF_CIF, SF_4CIF, SF_16CIF)
    pic->source_format = DEF_CODING_FORMAT;

   
// 参考帧频(Main内部用于控制帧率)
    ref_frame_rate = (float)DEF_REF_FRAME_RATE;
   
// 相对于原帧的丢帧率(也就是编码时丢弃视频输入文件帧的速度)
    chosen_frameskip = DEF_FRAMESKIP + 1;
   
// 相对于30fps参考帧率的原帧的丢帧率
   
// orig_frameskip * 30 fps = 视频输入文件的帧率,
   
// 这个帧率又叫原帧(original sequence)率
    orig_frameskip = DEF_ORIG_SKIP + 1;

   
//<! 帧率控制方法
#ifdef OFFLINE_RATE_CONTROL
    start_rate_control
= DEF_START_RATE_CONTROL;
#else
   
//<! 目标输出帧率, DEF_TARGET_FRAME_RATE =
    pic->target_frame_rate = (float)DEF_TARGET_FRAME_RATE;
#endif

    seqfilename[
0] = '\0';
    strcpy(streamname, DEF_STREAMNAME);
    strcpy(outputfile, DEF_OUTFILENAME);

   
//<! 是否保存原帧和重建帧的差异
    writediff = DEF_WRITE_DIFF;
   
//<! 是否TRACE运行过程
    trace = DEF_WRITE_TRACE;
   
//<! 在存在丢帧的情况下是否通过拷贝补全重建帧
    write_repeated = DEF_WRITE_REPEATED;
   
//<! 向量估算的长度
    pic->seek_dist = DEF_SEEK_DIST;
   
//<! 多少个编GOB后插入一个同步头,0为不再插入
    pic->use_gobsync = DEF_INSERT_SYNC;
   
//<! 从那一帧开始到那一帧结束帧率控制
    start = DEF_START_FRAME;
    end
= DEF_STOP_FRAME;

   
// 期望的码率, 0代表不控制
    targetrate = 0;
   
/* default is variable bit rate (fixed quantizer) will be used */

   
// 一些用于帧率控制的辅助统计参数
    frames = 0;            //I后以后的帧总数
    pframes = 0;            //P帧总数
    bframes = 0;            //B帧总数
    total_frames_passed = 0;//经过的帧数,包括被丢的帧
    pic->PB = 0;
    wcopies
= icopies = 1;    //因丢弃B或P帧而复制的数量 和 因丢弃I帧而复制的数量

    pic
->TR = 0;
    pic
->QP_mean = (float)0.0;



   
/* Process arguments */
   
for (i = 1; i < argc; i++) {
       
if (*(argv[i]) == '-') {
           
switch(*(++argv[i])) {
     
case 'a':
          start
= atoi(argv[++i]);
         
break;
     
case 'b':
          end
= atoi(argv[++i]);
         
break;
     
case 'S':
          chosen_frameskip
= atoi(argv[++i]) + 1;
         
break;
     
case 'O':
          orig_frameskip
= atoi(argv[++i]) + 1;
         
break;
     
case 's':
          pic
->seek_dist = atoi(argv[++i]);
         
break;
     
case 'o':
          strcpy(outputfile, argv[
++i]);
         
break;
     
case 'e':
          headerlength
= atoi(argv[++i]);
         
break;
     
case 'm':
          write_repeated
= ON;
         
break;
     
case 'i':
          strcpy(seqfilename, argv[
++i]);
         
break;
     
case 'q':
          QP
= atoi(argv[++i]);
         
if (QP > 31 || QP < 0) {
              fprintf(stderr,
"QP out of range - clipping it\n");
              QP
= mmin(31,mmax(0,QP));
          }

         
break;
     
case 'I':
          QPI
= atoi(argv[++i]);
         
if (QPI > 31 || QPI < 0) {
              fprintf(stderr,
"QP out of range - clipping it\n");
              QPI
= mmin(31,mmax(0,QPI));
          }

         
break;
     
case 'w':
          writediff
= ON;
         
break;
     
case 'B':
          strcpy(streamname, argv[
++i]);
         
break;
     
case 'h':
          Help();
          exit(
0);
         
break;
     
case 'H':
          AdvancedHelp();
          exit(
0);
         
break;
     
case 't':
          trace
= 1;
         
break;
     
case 'g':
          pic
->use_gobsync = atoi(argv[++i]);;
         
break;
     
case 'D':
         
/* note that the Unrestricted Motion Vector mode turns on
          both long_vectors and mv_outside_frame
*/

          pic
->unrestricted_mv_mode = ON;
          mv_outside_frame
= ON;
          long_vectors
= ON;
         
break;
     
case 'E':
          syntax_arith_coding
= ON;
         
break;
     
case 'F':
         
/* note that the Advanced Prediction mode turns on both
          advanced (8x8 vectors and OBMC) and mv_outside_frame
*/

         
/* the Extended Motion Vector mode is included in the
          Unrestricted Motion Vector mode, which of course can be
          use together with the Advanced Prediction mode
*/

          advanced
= ON;
          mv_outside_frame
= ON;
         
break;
     
case 'G':
          pb_frames
= ON;
         
break;
     
case 'Q':
          pic
->BQUANT = atoi(argv[++i]);
         
break;
     
case 'r':
          targetrate
= atoi(argv[++i]);
         
break;
#ifdef OFFLINE_RATE_CONTROL
     
case 'R':
          start_rate_control
= atoi(argv[++i]);
         
break;
#else
     
case 'R':
          pic
->target_frame_rate = (float)atof(argv[++i]);
         
break;
#endif
     
case 'Z':
          ref_frame_rate
= (float)atoi(argv[++i]);
         
break;
     
case 'x':
          pic
->source_format =  atoi(argv[++i]);
         
break;
     
default:
          fprintf(stderr,
"Illegal option: %c\n",*argv[i]);
          Help();
          exit(
-1);
         
break;
            }

        }

    }


   
switch (pic->source_format) {
 
case (SF_SQCIF):
      fprintf(stdout,
"Encoding format: SQCIF (128x96)\n");
      pels
= 128;
      lines
= 96;
     
break;
 
case (SF_QCIF):
      fprintf(stdout,
"Encoding format: QCIF (176x144)\n");
      pels
= 176;
      lines
= 144;
     
break;
 
case (SF_CIF):
      fprintf(stdout,
"Encoding format: CIF (352x288)\n");
      pels
= 352;
      lines
= 288;
     
break;
 
case (SF_4CIF):
      fprintf(stdout,
"Encoding format: 4CIF (704x576)\n");
      pels
= 704;
      lines
= 576;
     
break;
 
case (SF_16CIF):
      fprintf(stdout,
"Encoding format: 16CIF (1408x1152)\n");
      pels
= 1408;
      lines
= 1152;
     
break;
 
default:
      fprintf(stderr,
"Illegal coding format\n");
      exit(
-1);
    }

    cpels
= pels/2;

   
if (seqfilename[0] == '\0') {
        fprintf(stderr,
"Required input parameter \'-i <filename>\' missing\n");
        Help();
        exit(
-1);
    }


#ifndef OFFLINE_RATE_CONTROL
   
/* rate control variables */
   
// 要控制到达的码率
    pic->bit_rate = targetrate;
   
// 编码器源帧率,换句话说,也就是我们输入的文件的帧率
    pic->src_frame_rate = (int)(ref_frame_rate / orig_frameskip);
   
// 编码器的编码周期(Second/Frame)
    DelayBetweenFramesInSeconds = (float) 1.0/(float)pic->src_frame_rate;
    InitializeRateControl();
#endif

   
if (QP == 0 || QPI == 0) {
        fprintf(stderr,
"Warning:");
        fprintf(stderr,
"QP is zero. Bitstream will not be correctly decodable\n");
    }


   
if (ref_frame_rate != 25.0 && ref_frame_rate != 30.0) {
        fprintf(stderr,
"Warning: Reference frame rate should be 25 or 30 fps\n");
    }


   
// frame_rate为期望输出的码率
    frame_rate =  ref_frame_rate / (float)(orig_frameskip * chosen_frameskip);
#ifdef OFFLINE_RATE_CONTROL
    fprintf(stdout,
"Encoding frame rate  : %.2f\n", frame_rate);
#else
   
if (pic->bit_rate == 0)
        fprintf(stdout,
"Encoding frame rate  : %.2f\n", frame_rate);
   
else
        fprintf(stdout,
"Encoding frame rate  : variable\n");
#endif
    fprintf(stdout,
"Reference frame rate : %.2f\n", ref_frame_rate);
    fprintf(stdout,
"Orig. seq. frame rate: %.2f\n\n",
        ref_frame_rate
/ (float)orig_frameskip);

   
/* Open stream for writing */
    streamfile
= fopen (streamname, "wb");
   
if (streamname == NULL) {
        fprintf(stderr,
"Unable to open streamfile\n");
        exit(
-1);
    }
   

   
/* Initialize bitcounters */
    initbits ();

   
if (trace) {
        strcpy(tracefile,
"trace.intra");
       
/* Open trace-file for writing */
       
if ((tf = fopen(tracefile,"w")) == NULL) {
            fprintf(stderr,
"Unable to open tracefile (intra)\n");
            exit(
-1);
        }

    }

   
   
// 下面这个文件是用于输出从编码帧重建后的重建帧的.
    /* Clear output files */
   
if ((cleared = fopen(outputfile,"wb")) == NULL) {
        fprintf(stderr,
"Couldn't open outputfile: %s\n",outputfile);
        exit(
-1);
    }

   
else 
        fclose(cleared);

   
// 这个文件是用于保存原帧和重建帧差异的.
    if (writediff) {
       
if ((cleared = fopen(diff_filename,"wb")) == NULL) {
            fprintf(stderr,
"Couldn't open diff-file: %s\n",diff_filename);
            exit(
-1);
        }

       
else 
            fclose(cleared);
    }


   
// 读第一个原帧,这个帧必须采用帧内编码(I帧)
    /* Intra image */
    image
= ReadImage(seqfilename,start,headerlength);
    fprintf(stderr,
"Coding\n");
   
// 从读进来的数据填充pict_image结构.
    curr_image = FillImage(image);
   
// 选择编码格式的帧人编码
    pic->picture_coding_type = PCT_INTRA;
   
   
// 用设定的帧内编码量化矩阵参数给QUANT初始化
   
// 这个赋值我认为没用,因为CodeOneIntra函数也会通过参数给它赋值
    pic->QUANT = QPI;

   
// 进行I帧的编码,并输出重建帧
    curr_recon = CodeOneIntra(curr_image, QPI, bits, pic);

   
// 如果使用的算术编吗,应该要进行一些额外操作
   
// arith_used这个变量也是定义在算术编码的.c文件当中的,
   
// 这里只是引用,我不去研究他,因为我只会用它的哈夫曼编码
    if (arith_used) {
        bits
->header += encoder_flush();
        arith_used
= 0;
    }

   
// 填充数据使之字对齐
    bits->header += alignbits (); /* pictures shall be byte aligned */

    fprintf(stdout,
"Finished INTRA\n");

   
// 从原帧和重建帧进行信噪比测试,并输出相应的结果(res)或文件(writediff)
   
// 这种信息不一定每个程序都需要
    ComputeSNR(curr_image, curr_recon, res, writediff);
   
// 调整bits内部的比特统计数据(由各个部份的比特数累加得到总的比特数)
    AddBitsPicture(bits);
   
// 把信噪比信息输出到屏幕
    PrintSNR(res, 1);
   
// 把比特信息输出到屏幕
    PrintResult(bits, 1, 1);
   
// 把当前比特信息拷贝到I帧专用的比特信息变量intra_bits
    memcpy(intra_bits,bits,sizeof(Bits));
   
// 清零总比特,总的结果和B帧结果变量total_bits,total_res,b_res
    ZeroBits(total_bits);
    ZeroRes(total_res);
    ZeroRes(b_res);

   
   
/* number of seconds to encode */
   
   
// 计算编码一共需要的理论时间
   
//(end - start + chosen_frameskip): ??
   
//是因为下面这条first_frameskip = chosen_frameskip语句吗?
   
//那也应该是'-'才对呀,因为从start到start+chosen_frameskip帧从后面
   
//的程序看来是不编码的,想不明白.
   
//
   
//orig_frameskip/ref_frame_rate:   编码器编码的周期
   
//因些 seconds 为编码需要的时间
    seconds = (end - start + chosen_frameskip) * orig_frameskip/ ref_frame_rate;


   
if (trace) {
        strcpy(tracefile,
"trace");
        fclose(tf);
       
/* Open trace-file for writing */
       
if ((tf = fopen(tracefile,"w")) == NULL) {
            fprintf(stderr,
"Unable to open tracefile (non-intra)\n");
            exit(
-1);
        }

    }


   
/* compute first frameskip */
#ifdef OFFLINE_RATE_CONTROL
    first_frameskip
= chosen_frameskip;
    frameskip
= chosen_frameskip;
#else
   
// intra_bits->total: 编码帧的实际比特数
   
// DelayBetweenFramesInSeconds * pic->bit_rate: 根据计算的理论帧比特数
   
// 如果CommBacklog > 0 我们就得控制了,呵呵,当然~,也不能这么"眼浅".
    CommBacklog = intra_bits->total - 
        (
int)(DelayBetweenFramesInSeconds * pic->bit_rate);

   
if (pic->bit_rate == 0) {
       
// 如果没有流控,frameskip为chosen_frameskip
        frameskip = chosen_frameskip;
    }

   
else /* rate control is used */
       
// 如果存在流控,则计算这编码帧后为了保证码率而要丢掉的帧数
        frameskip = 1;
       
// 增加丢帧率直到比特差值CommBacklog小于一个理论帧比特数.
        while ( (int)(DelayBetweenFramesInSeconds*pic->bit_rate) <= CommBacklog) {
            CommBacklog
-= (int) ( DelayBetweenFramesInSeconds * pic->bit_rate );
            frameskip
+= 1;
        }

    }

   
//设定第一个I帧的后丢掉的帧数
    first_frameskip = frameskip;
#endif

   
//如果由于码率要求而丢太多编码帧就没有意义了.
    if (first_frameskip > 256)
        fprintf(stderr,
"Warning: frameskip > 256\n");

   
//好了,下面的编码类型全部选定的P帧,也就是向前预测编码.
    pic->picture_coding_type = PCT_INTER;
   
//量化矩阵参数也选择P帧的,当然,也可能和I帧用一样的.
    pic->QUANT = QP;
    bdist
= chosen_frameskip;

   
/* always encode the first frame after intra as P frame.
    This is not necessary, but something we chose to make
    the adaptive PB frames calculations a bit simpler
*/

   
if (pb_frames) {
        pic
->PB = 0;
        pdist
= 2*chosen_frameskip - bdist;
    }


   
   
if (write_repeated)
        icopies
= chosen_frameskip; //是否icopies = first_frameskip 更合理
   
//如果设了write_repeated,编译帧被丢的部份会通过拷贝保持未丢前水平.
    for (i = 0; i < icopies; i++)
        WriteImage(curr_recon,outputfile);
/* write wcopies frames to disk */


   
/***** Main loop *****/
   
//接下来就进行发送PB帧的循环内
   
//第一个frame应该是frame_no = start + first_frameskip
   
//其中start是我们指定的,first_frameskip为了补偿第一个已经发出去的I帧产生的码率增大的.
   
//增量为frameskip,因为现在我们还不知道其它帧的码率如何,只能引用I帧的情况,一会再调整.
    for (frame_no = start + first_frameskip; frame_no <= end;
        frame_no
+= frameskip) {

           
//更新上一原帧和上一重建帧
            prev_image = curr_image;
            prev_recon
= curr_recon;

           
/* Set QP to pic->QUANT from previous encoded picture */
            QP
= pic->QUANT;

           
// 当已经编码过一个P帧后PPFlag为真
            if (!PPFlag) {
               
//PB模式我们暂时不看
                if (pic->PB) {
                    bdist
= frameskip;
                    pdist
= 2*frameskip - bdist;
                    pic
->TRB = bdist * orig_frameskip;
                   
if (pic->TRB > 8)
                        fprintf(stdout,
"distance too large for B-frame\n");
                   
/* Read the frame to be coded as B */
                    image
= ReadImage(seqfilename,frame_no,headerlength);
                    B_image
= FillImage(image);
                    first_loop_finished
= 1;
                   
if (frame_no + pdist <= end) {
                        image
= ReadImage(seqfilename,frame_no + pdist,headerlength);
                    }

                   
else {
                        pic
->PB = 0; /* end of sequence, encode as P */
                        image
=  ReadImage(seqfilename,frame_no,headerlength);
                    }

                }

               
else {
                   
//读取目标源帧
                    image = ReadImage(seqfilename,frame_no,headerlength);
                }

               
//填充pict_image结够
                curr_image = FillImage(image);

               
//不看PB
                if (pic->PB) {
                   
if (pic->TRB > 8 || !NextTwoPB(curr_image, B_image, prev_image,
                        bdist, pdist, pic
->seek_dist)) {
                           
/* curr_image and B_image were not suitable to be coded
                            as a PB-frame - encoding as two P-frames instead
*/

                            pic
->PB = 0;
#ifdef OFFLINE_RATE_CONTROL
                            stored_image
= curr_image;
#else
                            FreeImage(curr_image);
#endif
                            frameskip
= bdist;

                            curr_image
= B_image;
                            PPFlag
= 1;
                        }

                   
else {
                        frame_no
+= pdist;
                    }

                }

            }

           
else {
               
/* PPFlag is set when the second of the two P-frames
                is due to be coded
*/

               
//这已经是第二以上编采用用P帧编码了.
               
//其实我们可以看到,做了这么多的判断都是因为有了PB帧,不然很简单
#ifdef OFFLINE_RATE_CONTROL
                curr_image
= stored_image;
#else
               
//还是读源帧,然后填充
                image =  ReadImage(seqfilename,frame_no,headerlength);
                curr_image
= FillImage(image);
#endif
                pic
->PB = 0;
                PPFlag
= 0;
            }


           
/* Temporal Reference is the distance between encoded frames compared
            the reference picture rate which is 25.0 or 30 fps
*/

           
//TR为当前编码帧对于参考帧率(30或25fps)的距离.
           
//不考率B帧的情况下.
           
// TR+= frameskip * orig_frameskip;
            pic->TR += (( (frameskip+(pic->PB?pdist:0)) *orig_frameskip) % 256);
           
if (frameskip+(pic->PB?pdist:0) > 256)
                fprintf(stdout,
"Warning: frameskip > 256\n");


           
//调整帧的统计值
            frames += (pic->PB ? 2: 1);
            bframes
+= (pic->PB ? 1 : 0);
            pframes
+= 1;

           
if (pic->PB) { /* Code two frames as a PB-frame */
                B_recon
= InitImage(pels*lines);
                fprintf(stdout,
"Coding PB frames %d and %d ",
                    frame_no
- pdist, frame_no);
                fflush(stdout);
            }

           
else { /* Code the next frame as a normal P-frame */
                fprintf(stdout,
"Coding P frame %d ", frame_no);
                fflush(stdout);
            }

           
           
//初始化curr_recon.分配空间??
            curr_recon = InitImage(pels*lines);

           
//开始进行编码.
            CodeOneOrTwo(curr_image, B_image, prev_image, prev_recon,
                QP, (bdist
+pdist)*orig_frameskip, bits, pic,
                B_recon, curr_recon);


            fprintf(stdout,
"done\n");
           
if (targetrate != 0)
                fprintf(stdout,
"Inter QP: %d\n", QP);
            fflush(stdout);

           
if (arith_used) {
                bits
->header += encoder_flush();
                arith_used
= 0;
            }


            bits
->header += alignbits ();  /* pictures shall be byte aligned */
            AddBitsPicture(bits);
            AddBits(total_bits, bits);

#ifndef OFFLINE_RATE_CONTROL
           
//重新计算码率的各控制参数
            if (pic->bit_rate != 0 && pic->PB)
                CommBacklog
-= (int)
                ( DelayBetweenFramesInSeconds
*pic->bit_rate ) * pdist;

           
if (pic->bit_rate != 0) {
               
//根据TMN5 文档进行码率控制,文档我Google了半天都找不到.:-(
               
//应该是调整量化矩阵
                UpdateRateControl(bits->total);

               
//先算出超出来的部分
                CommBacklog += bits->total;
                frameskip
= 1;
                CommBacklog
-= (int)
                    (frameskip
* DelayBetweenFramesInSeconds *pic->bit_rate);
               
//再来算跳过几个帧才能把超出的比特跳过多少个帧来能补回来
                while ( (int)(DelayBetweenFramesInSeconds*pic->bit_rate) <= CommBacklog)
               
{
                    CommBacklog
-= (int) ( DelayBetweenFramesInSeconds * pic->bit_rate );
                    frameskip
+= 1;
                }

            }

#else
           
/* Aim for the targetrate with a once per frame rate control scheme */
           
if (targetrate != 0)
               
if (frame_no - start > (end - start) * start_rate_control/100.0)
                   
/* when generating the MPEG-4 anchors, rate control was started
                    after 70% of the sequence was finished.
                    Set start_rate_control with option "-R <n>"
*/


                    pic
->QUANT = FrameUpdateQP(total_bits->total + intra_bits->total,
                    bits
->total / (pic->PB?2:1),
                    (end
-frame_no) / chosen_frameskip + PPFlag,
                    QP, targetrate, seconds);
            frameskip
= chosen_frameskip;
#endif
           
//先不看PB
            if (pic->PB) {
               
if (write_repeated)
                    wcopies
= pdist;
               
for (i = 0; i < wcopies; i++)
                    WriteImage(B_recon,outputfile);
/* write wcopies frames to disk */
                ComputeSNR(B_image, B_recon, res, writediff);
                fprintf(stdout,
"Results for B-frame:\n");
                AddRes(b_res,res,pic);
                PrintSNR(res,
1);
                FreeImage(B_image);
                FreeImage(B_recon);
            }


           
//跳过的帧在写输出文件时拷贝回来.
            if (write_repeated)
                wcopies
= (pb_frames ? bdist : frameskip);
           
for (i = 0; i < wcopies; i++)
                WriteImage(curr_recon,outputfile);
/* write wcopies frames to disk */

           
if (pb_frames)
                pic
->PB = 1;

           
//计算信噪比登参数
            ComputeSNR(curr_image, curr_recon, res, writediff);
            fprintf(stdout,
"Results for P-frame:\n");
            AddRes(total_res,res,pic);
            PrintSNR(res,
1);
            PrintResult(bits,
1, 1);
            FreeImage(prev_image);
            FreeImage(prev_recon);
            fflush(stdout);
        }

       
/***** end of main loop *****/

       
/* Closing files */
        fclose (streamfile);
       
if (trace) {
            fclose(tf);
        }


       
/* Print total results */
       
//total_frames_passed是这么吗,扣掉first_frameskip,
       
//那中间那些被跳过的帧呢?
       
//这一条和上面的那一条
       
//"seconds = (end - start + chosen_frameskip) * orig_frameskip/ ref_frame_rate;"
       
//都想不明白.

       
//从fprintf(stdout,"Frames saved : %d predicted + %d intra\n",
       
//        total_frames_passed,icopies);
       
//这名话推测total_frames_passed是没有包话前面的I帧以及它的拷贝帧的.

       
//要如下的数合理,只能是frames, bframes, pframes, total_frames_passed 都按重0算起
       
        total_frames_passed
= frame_no - start - first_frameskip;

        fprintf(stdout,
"\n==== TOTAL ====\n");
        fprintf(stdout,
"for %d images of %s\n", frames, seqfilename);

       
if (frames != 0) {
           
if (write_repeated)
                fprintf(stdout,
"Frames saved : %d predicted + %d intra\n",
                total_frames_passed,icopies);

            fprintf(stdout,
"--------------\n");

           
if (pb_frames && bframes != 0) {
                fprintf(stdout,
"SNR for %d B-frames:\n",bframes);
                PrintSNR(b_res,bframes);
            }


            fprintf(stdout,
"SNR for %d P-frames:\n",pframes);
            PrintSNR(total_res,pframes);

            PrintResult(total_bits, pframes, frames);

           
if (targetrate != 0 || pic->bit_rate != 0)
                fprintf(stdout,
"Original seq time: %.2f (%.2f) sec\n",
                (total_frames_passed
+ first_frameskip) /
                ref_frame_rate
* orig_frameskip,
                total_frames_passed
/
                ref_frame_rate
* orig_frameskip);

            fprintf(stdout,
"Mean quantizer   : %.2f\n", total_res->QP_mean/pframes);

#if 0
            fprintf(stdout,
"Total frames     : %3d (%3d)\n",
                total_frames_passed
+ first_frameskip,
                total_frames_passed);
#endif

            fprintf(stdout,
"Encoded frames   : %3d (%3d)\n",
                frames
+ 1,
                frames);

            mean_frame_rate
= frames  / (float)total_frames_passed * 
                ref_frame_rate
/ (float)orig_frameskip;

            fprintf(stdout,
"Mean frame rate  : %.2f Hz\n", mean_frame_rate);

           
if (targetrate != 0)
                fprintf(stdout,
"Target bit rate  : %.2f kbit/sec\n",
                targetrate
/1000.0);

            fprintf(stdout,
"Obtained bit rate: %.2f (%.2f) kbit/sec\n",
                (total_bits
->total + intra_bits->total) / 
                ((total_frames_passed
+ first_frameskip) / 
                ref_frame_rate
* orig_frameskip)/1000.0,
                (total_bits
->total / (float)frames) * mean_frame_rate/1000.0);

            fprintf(stdout,
"============================================\n");

        }

#if 0
        fprintf(stdout,
"Total number of bits: %d (%d)\n",
            total_bits
->total + intra_bits->total,
            (total_bits
->total + intra_bits->total) / 8);
#endif

       
/* Free memory */
        FreeImage(curr_recon);
        FreeImage(curr_image);
        free(streamname);
        free(seqfilename);
        free(outputfile);
        free(tracefile);
        free(bits);
        free(total_bits);
        free(intra_bits);
        free(res);
        free(total_res);
        free(b_res);
        free(pic);
        exit(
0);
}


/**********************************************************************
*
*    Name:        NextTwoPB
*    Description:    Decides whether or not to code the next
*                      two images as PB
*      Speed:          This is not a very smart solution considering
*                      the encoding speed, since motion vectors
*                      have to be calculation several times. It
*                      can be done together with the normal
*                      motion vector search, or a tree search
*                      instead of a full search can be used.
*   
*    Input:            pointers to previous image, potential B-
*                      and P-image, frame distances
*    Returns:        1 for yes, 0 otherwise
*    Side effects:
*
*    Date: 950824    Author: Karl.Lillevold@nta.no
*
**********************************************************************
*/


int NextTwoPB(PictImage *next2, PictImage *next1, PictImage *prev,
             
int bskip, int pskip, int seek_dist)
{
   
int adv_is_on = 0, mof_is_on = 0, lv_is_on = 0;
   
int psad1, psad2, bsad, psad;
    MotionVector
*MV[6][MBR+1][MBC+2];
    MotionVector
*mvp, *mvbf, *mvbb;
   
int x,y;
   
int i,j,k,tmp;

   
/* Temporarily disable some options to simplify motion estimation */
   
if (advanced) {
        advanced
= OFF;
        adv_is_on
= ON;
    }

   
if (mv_outside_frame) {
        mv_outside_frame
= OFF;
        mof_is_on
= ON;
    }

   
if (long_vectors) {
        long_vectors
= OFF;
        lv_is_on
= ON;
    }


   
for (j = 1; j <= (lines>>4); j++)
       
for (i = 1; i <= (pels>>4); i++)
           
for (k = 0; k < 3; k++) {
                MV[k][j][i]
= (MotionVector *)calloc(1,sizeof(MotionVector));
               
/* calloc to avoid Checker warnings about reading of
                unitizalized memory in the memcpy's below
*/

            }


            mvbf
= (MotionVector *)malloc(sizeof(MotionVector));
            mvbb
= (MotionVector *)malloc(sizeof(MotionVector));

            psad
= 0;
            psad1
= 0;
            psad2
= 0;
            bsad
= 0;

           
/* Integer motion estimation */
           
for ( j = 1; j < lines/MB_SIZE - 1; j++) {
               
for ( i = 1; i < pels/MB_SIZE - 1 ; i++) {
                    x
= i*MB_SIZE;
                    y
= j*MB_SIZE;

                   
/* picture order: prev -> next1 -> next2 */
                   
/* next1 and next2 can be coded as PB or PP */
                   
/* prev is the previous encoded picture */

                   
/* computes vectors (prev <- next2) */
                    MotionEstimation(next2
->lum,prev->lum,x,y,0,0,seek_dist,MV,&tmp);
                   
if (MV[0][j+1][i+1]->x == 0 && MV[0][j+1][i+1]->y == 0)
                        MV[
0][j+1][i+1]->min_error += PREF_NULL_VEC;
                   
/* not necessary to prefer zero vector here */
                    memcpy(MV[
2][j+1][i+1],MV[0][j+1][i+1],sizeof(MotionVector));

                   
/* computes sad(prev <- next1) */
                    MotionEstimation(next1
->lum,prev->lum,x,y,0,0,seek_dist,MV,&tmp);
                   
if (MV[0][j+1][i+1]->x == 0 && MV[0][j+1][i+1]->y == 0)
                        MV[
0][j+1][i+1]->min_error += PREF_NULL_VEC;
                    memcpy(MV[
1][j+1][i+1],MV[0][j+1][i+1],sizeof(MotionVector));

                   
/* computes vectors for (next1 <- next2) */
                    MotionEstimation(next2
->lum,next1->lum,x,y,0,0,seek_dist,MV,&tmp);
                   
if (MV[0][j+1][i+1]->x == 0 && MV[0][j+1][i+1]->y == 0)
                        MV[
0][j+1][i+1]->min_error += PREF_NULL_VEC;

                   
/* scales vectors for (prev <- next2 ) */
                    mvp
= MV[2][j+1][i+1];
                    mvbf
->x =   bskip * mvp->x / (bskip + pskip);
                    mvbb
->x = - pskip * mvp->x / (bskip + pskip);
                    mvbf
->y =   bskip * mvp->y / (bskip + pskip);
                    mvbb
->y = - pskip * mvp->y / (bskip + pskip);

                    psad1
+= MV[0][j+1][i+1]->min_error;
                    psad2
+= MV[1][j+1][i+1]->min_error;
                    psad
+=  mvp->min_error;

                   
/* computes sad(prev <- next1 -> next2) */
                    bsad
+= SAD_MB_Bidir(next1->lum + x + y*pels,
                        next2
->lum + x + mvbb->x + (y + mvbb->y)*pels,
                        prev
->lum + x + mvbf->x + (y + mvbf->y)*pels,
                        pels, INT_MAX);
                }

            }


           
for (j = 1; j <= (lines>>4); j++)
               
for (i = 1; i <= (pels>>4); i++)
                   
for (k = 0; k < 3; k++)
                        free(MV[k][j][i]);
            free(mvbf);
            free(mvbb);

           
/* restore advanced parameters */
            advanced
= adv_is_on;
            mv_outside_frame
= mof_is_on;
            long_vectors
= lv_is_on;

           
/* do the decision */
           
if (bsad < (psad1+psad2)/2)
                fprintf(stdout,
"Chose PB - bsad %d, psad %d\n",
                bsad, (psad1
+psad2)/2);
           
else
                fprintf(stdout,
"Chose PP  - bsad %d, psad %d\n",
                bsad, (psad1
+psad2)/2);

           
if (bsad < (psad1 + psad2)/2)
               
return 1;
           
else 
               
return 0;
}




/**********************************************************************
*
*    Name:        Help
*    Description:        Prints usage
*   
*
**********************************************************************
*/


void Help()
{
    fprintf(stdout,
"Usage:\ttmn [options] -i <filename> [more options]\n");
    fprintf(stdout,
"Options:\n");
    fprintf(stdout,
"\t-i <filename> original sequence [required parameter]\n");
    fprintf(stdout,
"\t-o <filename> reconstructed frames [%s]\n",
        DEF_OUTFILENAME);
    fprintf(stdout,
"\t-B <filename> filename for bitstream [%s]\n",
        DEF_STREAMNAME);
    fprintf(stdout,
"\t-a <n> image to start at [%d]\n",
        DEF_START_FRAME);
    fprintf(stdout,
"\t-b <n> image to stop at [%d]\n",
        DEF_STOP_FRAME);
    fprintf(stdout,
"\t-x <n> coding format [%d]\n",DEF_CODING_FORMAT);
    fprintf(stdout,
"\t-q <n> (1..31) quantization parameter QP [%d]\n",
        DEF_INTER_QUANT);
    fprintf(stdout,
"\t-I <n> (1..31) QP for first frame [%d]\n",
        DEF_INTRA_QUANT);
    fprintf(stdout,
"\t-r <n> target bitrate in bits/s, default is variable bitrate\n");
    fprintf(stdout,
"\t-S <n> frames to skip between each encoded frame [%d]\n",
        DEF_FRAMESKIP);
    fprintf(stdout,
"\t-D use unrestricted motion vector mode (annex D) [%s]\n",
        DEF_UMV_MODE
? "ON" : "OFF");
    fprintf(stdout,
"\t-E use syntax-based arithmetic coding (annex E) [%s]\n",
        DEF_SAC_MODE
? "ON" : "OFF");
    fprintf(stdout,
"\t-F use advanced prediction mode (annex F) [%s]\n",
        DEF_ADV_MODE
? "ON" : "OFF");
    fprintf(stdout,
"\t-G use PB-frames (annex G) [%s]\n",
        DEF_PBF_MODE
? "ON" : "OFF");
    fprintf(stdout,
"\t-h Prints simple help\n");
    fprintf(stdout,
"\t-H Prints advanced help\n");
    fprintf(stdout,
"\n\tDefault filenames and other options in square brackets \n\tare chosen in config.h\n");
   
return;
}


void AdvancedHelp()
{
    fprintf(stdout,
"Usage:\ttmn [options] -i <filename> [more options]\n");
    fprintf(stdout,
"Options:\n");
    fprintf(stdout,
"\t-i <filename> original sequence [required parameter]\n");
    fprintf(stdout,
"\t-o <filename> reconstructed frames [%s]\n",
        DEF_OUTFILENAME);
    fprintf(stdout,
"\t-B <filename> filename for bitstream [%s]\n",
        DEF_STREAMNAME);
    fprintf(stdout,
"\t-a <n> image to start at [%d]\n",
        DEF_START_FRAME);
    fprintf(stdout,
"\t-b <n> image to stop at [%d]\n",
        DEF_STOP_FRAME);
    fprintf(stdout,
"\t-x <n> coding format [%d]\n",DEF_CODING_FORMAT);
    fprintf(stdout,
"\t       n=1: SQCIF n=2: QCIF n=3: CIF n=4: 4CIF n=5: 16CIF\n");
    fprintf(stdout,
"\t           128x96   176x144  352x288   704x576  1408x1152\n");
    fprintf(stdout,
"\t-s <n> (0..15) integer pel search window [%d]\n",
        DEF_SEEK_DIST);
    fprintf(stdout,
"\t-q <n> (1..31) quantization parameter QP [%d]\n",
        DEF_INTER_QUANT);
    fprintf(stdout,
"\t-I <n> (1..31) QP for first frame [%d]\n",
        DEF_INTRA_QUANT);
    fprintf(stdout,
"\t-r <n> target bitrate in bits/s, default is variable bitrate\n");
#ifdef OFFLINE_RATE_CONTROL
    fprintf(stdout,
"\t   -R <n> start rate control after n%% of sequence [%d]\n",
        DEF_START_RATE_CONTROL);
#else
    fprintf(stdout,
"\t   -R <f> target frame rate [%.2f]\n",
        DEF_TARGET_FRAME_RATE);
#endif
    fprintf(stdout,
"\t-S <n> frames to skip between each encoded frame [%d]\n",
        DEF_FRAMESKIP);
    fprintf(stdout,
"\t-Z <n> reference frame rate (25 or 30 fps) [%.1f]\n",
        DEF_REF_FRAME_RATE);
    fprintf(stdout,
"\t-O <n> frames skipped in original compared to reference frame rate [%d]\n", DEF_ORIG_SKIP);
    fprintf(stdout,
"\t-e <n> original sequence has n bytes header [%d]\n",
        DEF_HEADERLENGTH);
    fprintf(stdout,
"\t-g <n> insert sync after each n GOB (slice) [%d]\n",
        DEF_INSERT_SYNC);
    fprintf(stdout,
"\t       zero above means no extra syncs inserted\n");
    fprintf(stdout,
"\t-w write difference image to file \"%s\" [%s]\n",
        DEF_DIFFILENAME,
        DEF_WRITE_DIFF
? "ON" : "OFF");
    fprintf(stdout,
"\t-m write repeated reconstructed frames to disk [%s]\n",
        DEF_WRITE_REPEATED
? "ON" : "OFF");
    fprintf(stdout,
"\t-t write trace to tracefile trace.intra/trace [%s]\n",
        DEF_WRITE_TRACE
? "ON" : "OFF");
    fprintf(stdout,
"\t-D use unrestricted motion vector mode (annex D) [%s]\n",
        DEF_UMV_MODE
? "ON" : "OFF");
    fprintf(stdout,
"\t-E use syntax-based arithmetic coding (annex E) [%s]\n",
        DEF_SAC_MODE
? "ON" : "OFF");
    fprintf(stdout,
"\t-F use advanced prediction mode (annex F) [%s]\n",
        DEF_ADV_MODE
? "ON" : "OFF");
    fprintf(stdout,
"\t-G use PB-frames (annex G) [%s]\n",
        DEF_PBF_MODE
? "ON" : "OFF");
    fprintf(stdout,
"\t   -Q <n> (0..3) BQUANT parameter [%d]\n",DEF_BQUANT);
    fprintf(stdout,
"\t-h Prints simple help\n");
    fprintf(stdout,
"\t-H Prints advanced help\n");
    fprintf(stdout,
"\n\tDefault filenames and other options in square brackets \n\tare chosen in config.h\n");
   
return;
}


/**********************************************************************
*
*    Name:        PrintResult
*    Description:    add bits and prints results
*   
*    Input:        Bits struct
*       
*    Returns:   
*    Side effects:   
*
*    Date: 940116    Author:    Karl.Lillevold@nta.no
*
**********************************************************************
*/



void PrintResult(Bits *bits,int num_units, int num)
{
    fprintf(stdout,
"# intra   : %d\n", bits->no_intra/num_units);
    fprintf(stdout,
"# inter   : %d\n", bits->no_inter/num_units);
    fprintf(stdout,
"# inter4v : %d\n", bits->no_inter4v/num_units);
    fprintf(stdout,
"--------------\n");
    fprintf(stdout,
"Coeff_Y: %d\n", bits->Y/num);
    fprintf(stdout,
"Coeff_C: %d\n", bits->C/num);
    fprintf(stdout,
"Vectors: %d\n", bits->vec/num);
    fprintf(stdout,
"CBPY   : %d\n", bits->CBPY/num);
    fprintf(stdout,
"MCBPC  : %d\n", bits->CBPCM/num);
    fprintf(stdout,
"MODB   : %d\n", bits->MODB/num);
    fprintf(stdout,
"CBPB   : %d\n", bits->CBPB/num);
    fprintf(stdout,
"COD    : %d\n", bits->COD/num);
    fprintf(stdout,
"DQUANT : %d\n", bits->DQUANT/num);
    fprintf(stdout,
"header : %d\n", bits->header/num);
    fprintf(stdout,
"==============\n");
    fprintf(stdout,
"Total  : %d\n", bits->total/num);
    fprintf(stdout,
"\n");
   
return;
}


void PrintSNR(Results *res, int num)
{
    fprintf(stdout,
"SNR_Y  : %.2f\n", res->SNR_l/num);
    fprintf(stdout,
"SNR_Cb : %.2f\n", res->SNR_Cb/num);
    fprintf(stdout,
"SNR_Cr : %.2f\n", res->SNR_Cr/num);
    fprintf(stdout,
"--------------\n");
   
return;
}


Feedback

# re: H.263 视频 标准编码库 TMN2.0EDemo程序中文注释  回复  更多评论   

2008-11-27 23:37 by 李肖
你好,看了你这篇.我是用的tmn3.0的编译后对其中的一个yuv文件编码得到的263文件播放没有任何东西,还有想问下,用这个编码后的我想保存为一个通用文件格式,比如3gp,不知你有没有什么好的方法
email :liyigang86@163.com
没有找到你的邮箱不好意思

# re: H.263 视频 标准编码库 TMN2.0EDemo程序中文注释  回复  更多评论   

2008-12-04 11:19 by 猫头鹰
@李肖
用tmn编码后,你得到的只是H263编码流,和我们一般说的视频文件还是有差别的,视频文件会对编码流再封装。当前流行的播放器好像都不能直接播发H263,H263一般只用在网络视频转输上,如VOIP等网络视频应用。看到你的回复后,我查了查3GP格式,现在H263转3GP的你能用FFMPEG来转。因为H263(或者H264编码流)一般不会直接保存为通用视频文件格式,所以它的转换器是很少的,或者说没有。我不知道你最终想研究的是H263还是3GP,如果是H263的话,一般不会找所谓的播放器,而是自己做软件,解码你可以用TMNDEC库。

# re: H.263 视频 标准编码库 TMN2.0EDemo程序中文注释  回复  更多评论   

2008-12-04 22:58 by 李肖
我正在做的是个录像的,在wince下的,只找到h263这个能在这个平台下用的,其他的编码库像xvid,以及你说的ffmpeg我都没有移植成功(编译是过了,但没办法调用生成的库,呵呵).
h263的我只找到一个是这个tmn3.0的,还有一个是libr263的,但都没有编码成功,特别是tmn3.0的用那个demo程序在vc下编译运行生成的文件没有任何东西,不知是什么原因,能否说下

# re: H.263 视频 标准编码库 TMN2.0EDemo程序中文注释[未登录]  回复  更多评论   

2008-12-14 21:35 by 猫头鹰
@李肖
我window下我们一般会用Direct X进行视频开发的,我不知Win
CE是否这样。我是用linux的,不好意思。“特别是tmn3.0的用那个demo程序在vc下编译运行生成的文件没有任何东西”你是指生成不了程序还是生成的程序不能用?

# re: H.263 视频 标准编码库 TMN2.0EDemo程序中文注释  回复  更多评论   

2008-12-14 22:52 by 李肖
没有任何东西我是指对一个原始的码流编码生成的.263文件,是空的,可否把你在linux下的用的makfile文件发给我看下吗,不过如果可以的话,最好能把你用的这个版本也的源码也发过来用下,因为我手上是3.0的,我比较了下,有点不一样

# re: H.263 视频 标准编码库 TMN2.0EDemo程序中文注释  回复  更多评论   

2009-03-30 20:34 by GKK
你好,我在研究TMN2.0时候发现encode中的io.c文件似乎有问题,类似

im_file = fopen(tmp_Y,"rb");

这样的语句在逻辑和语言上都有问题,如果有时间,希望能得要你的看法.

# re: H.263 视频 标准编码库 TMN2.0EDemo程序中文注释  回复  更多评论   

2009-04-27 21:04 by 猫头鹰
@GKK
我的TMN2.0代码没有encode这个文件夹呀,io.c也没有这条语句。一方面你看看你你的代码是不是官方的。另外一方面我再找找原码包看看(我现在看的时原来解在好的).

# re: H.263 视频 标准编码库 TMN2.0EDemo程序中文注释  回复  更多评论   

2009-09-10 16:54 by 请教
刚刚接触h.263,请问yuv视频流,用tmn3 encode编码成什么格式的文件才能用tmn decode 解码?谢谢!

# re: H.263 视频 标准编码库 TMN2.0EDemo程序中文注释  回复  更多评论   

2009-12-21 15:43 by cnp
看到楼主的 文章 很有用~
最近 在研究Mobile ppc 摄像头拍摄后直接编码成h263
请问楼主 或者其他朋友能提供一个 Tmn/tmndec2.0库源码下载地址么? email给我也行 先谢谢了 email:cnp12@163.com
只有注册用户登录后才能发表评论。