📄 coder.c
字号:
/************************************************************************ *主编码程序* ************************************************************************/#include"sim.h"/********************************************************************** * * 函数: CodeOneOrTwo * 功能: 编码正常的一帧图像或两帧PB模式的图像 * 输入: 图像数据指针,前一帧图像指针,重构图像指针,量化参数等 * 返回值:重构图像的指针 ***********************************************************************/void CodeOneOrTwo(PictImage *curr, PictImage *B_image, PictImage *prev, PictImage *pr, int QP, int frameskip, Bits *bits, Pict *pic, PictImage *B_recon, PictImage *recon){ unsigned char *prev_ipol,*pi_edge=NULL,*pi,*orig_lum; PictImage *prev_recon=NULL, *pr_edge=NULL; MotionVector *MV[6][MBR+1][MBC+2]; MotionVector ZERO = {0,0,0,0,0}; MB_Structure *recon_data_P; MB_Structure *recon_data_B=NULL; MB_Structure *diff; int *qcoeff_P; int *qcoeff_B=NULL; int Mode,B; int CBP, CBPB=0; int bquant[] = {5,6,7,8}; int QP_B; int newgob; int i,j,k; /* 编码所需的存储 */ float QP_cumulative = (float)0.0; int abs_mb_num = 0, QuantChangePostponed = 0; int QP_new, QP_prev, dquant, QP_xmitted=QP; ZeroBits(bits); /* 内插图像 */ if (mv_outside_frame) { if (long_vectors) { /* 如果编码器使用了扩展的运动向量范围,那么运动向量可能
会进一步指向视频帧外,从而超出一个正常的搜索范围。
因此边界图像将不得不扩大尺寸 */ B = 16; } else { /* 正常搜索范围 */ B = 8; } pi_edge = (unsigned char *)malloc(sizeof(char)*(pels+4*B)*(lines+4*B)); if (pi_edge == NULL) { fprintf(stderr,"Couldn't allocate memory for pi_edge\n"); exit(-1); } MakeEdgeImage(pr->lum,pi_edge + (pels + 4*B)*2*B+2*B,pels,lines,2*B); pi = InterpolateImage(pi_edge, pels+4*B, lines+4*B); free(pi_edge); prev_ipol = pi + (2*pels + 8*B) * 4*B + 4*B; /* 非内插图像的亮度信息 */ pr_edge = InitImage((pels+4*B)*(lines+4*B)); MakeEdgeImage(prev->lum, pr_edge->lum + (pels + 4*B)*2*B+2*B, pels,lines,2*B); orig_lum = pr_edge->lum + (pels + 4*B)*2*B+2*B; /* 非内插图像 */ MakeEdgeImage(pr->lum,pr_edge->lum + (pels+4*B)*2*B + 2*B,pels,lines,2*B); MakeEdgeImage(pr->Cr,pr_edge->Cr + (pels/2 + 2*B)*B + B,pels/2,lines/2,B); MakeEdgeImage(pr->Cb,pr_edge->Cb + (pels/2 + 2*B)*B + B,pels/2,lines/2,B); prev_recon = (PictImage *)malloc(sizeof(PictImage)); prev_recon->lum = pr_edge->lum + (pels + 4*B)*2*B + 2*B; prev_recon->Cr = pr_edge->Cr + (pels/2 + 2*B)*B + B; prev_recon->Cb = pr_edge->Cb + (pels/2 + 2*B)*B + B; } else { pi = InterpolateImage(pr->lum,pels,lines); prev_ipol = pi; prev_recon = pr; orig_lum = prev->lum; } /* 标记帧外的PMV信息 */ for (i = 1; i < (pels>>4)+1; i++) { for (k = 0; k < 6; k++) { MV[k][0][i] = (MotionVector *)malloc(sizeof(MotionVector)); MarkVec(MV[k][0][i]); } MV[0][0][i]->Mode = MODE_INTRA; } /* 帧外的PMVzero out PMV's outside the frame */ for (i = 0; i < (lines>>4)+1; i++) { for (k = 0; k < 6; k++) { MV[k][i][0] = (MotionVector *)malloc(sizeof(MotionVector)); ZeroVec(MV[k][i][0]); MV[k][i][(pels>>4)+1] = (MotionVector *)malloc(sizeof(MotionVector)); ZeroVec(MV[k][i][(pels>>4)+1]); } MV[0][i][0]->Mode = MODE_INTRA; MV[0][i][(pels>>4)+1]->Mode = MODE_INTRA; } /* 整像素精度和半像素精度的运动估计*/ MotionEstimatePicture(curr->lum,prev_recon->lum,prev_ipol, pic->seek_dist,MV, pic->use_gobsync); /* 注意:整像素精度的运动估计是在之前已重构图像的基础上进行的,
而不是基于之前的原始视频帧图像。这种方法较为有效。此外,
如利用orig_lum取代prev_recon->lum的话,会造成运动估计中一些
不可逆转的错误,这是因为零向量的SAD值在半像素精度搜索中不会
重新计算。半像素精度的运动估计计算总是基于之前已重构图像进行的。 */#ifndef OFFLINE_RATE_CONTROL if (pic->bit_rate != 0) { /* 率控制的初始化 */ QP_new = InitializeQuantizer(PCT_INTER, (float)pic->bit_rate, (pic->PB ? pic->target_frame_rate/2 : pic->target_frame_rate), pic->QP_mean); QP_xmitted = QP_prev = QP_new; } else { QP_new = QP_xmitted = QP_prev = QP; /* 复制量化参数的值 */ }#else QP_new = QP_xmitted = QP_prev = QP; /* 复制量化参数的值 */#endif dquant = 0; for ( j = 0; j < lines/MB_SIZE; j++) {#ifndef OFFLINE_RATE_CONTROL if (pic->bit_rate != 0) { /* 每行开始编码之前更新量化参数 */ AddBitsPicture(bits); QP_new = UpdateQuantizer(abs_mb_num, pic->QP_mean, PCT_INTER, (float)pic->bit_rate, pels/MB_SIZE, lines/MB_SIZE, bits->total); }#endif newgob = 0; if (j == 0) { pic->QUANT = QP_new; bits->header += CountBitsPicture(pic); QP_xmitted = QP_prev = QP_new; } else if (pic->use_gobsync && j%pic->use_gobsync == 0) { bits->header += CountBitsSlice(j,QP_new); /* insert gob sync */ QP_xmitted = QP_prev = QP_new; newgob = 1; } for ( i = 0; i < pels/MB_SIZE; i++) { /* 更新解量化,检查并保证其限度的正确性 */ dquant = QP_new - QP_prev; if (dquant != 0 && i != 0 && MV[0][j+1][i+1]->Mode == MODE_INTER4V) { /* 在使用8×8向量的时候,编码器几乎不可能改变量化器参数。
此外,由于已编码宏块需要使用8×8运动向量来完成编码,
因此在本阶段中该运动向量不能置0关闭。基于上述理由,
编码器在检测到第一个不含8×8运动向量的宏块后才去更新
量化器 */ dquant = 0; QP_xmitted = QP_prev; QuantChangePostponed = 1; } else { QP_xmitted = QP_new; QuantChangePostponed = 0; } if (dquant > 2) { dquant = 2; QP_xmitted = QP_prev + dquant;} if (dquant < -2) { dquant = -2; QP_xmitted = QP_prev + dquant;} pic->DQUANT = dquant; /* 如果dquant != 0,则修改量化器模式 */ Mode = ModifyMode(MV[0][j+1][i+1]->Mode,pic->DQUANT); MV[0][j+1][i+1]->Mode = Mode; pic->MB = i + j * (pels/MB_SIZE); if (Mode == MODE_INTER || Mode == MODE_INTER_Q || Mode==MODE_INTER4V) { /* 预测P帧的宏块 */ diff = Predict_P(curr,prev_recon,prev_ipol, i*MB_SIZE,j*MB_SIZE,MV,pic->PB); } else { diff = (MB_Structure *)malloc(sizeof(MB_Structure)); FillLumBlock(i*MB_SIZE, j*MB_SIZE, curr, diff); FillChromBlock(i*MB_SIZE, j*MB_SIZE, curr, diff); } /* P帧宏块和帧内宏块 */ qcoeff_P = MB_Encode(diff, QP_xmitted, Mode); CBP = FindCBP(qcoeff_P, Mode, 64); if (CBP == 0 && (Mode == MODE_INTER || Mode == MODE_INTER_Q)) ZeroMBlock(diff); else MB_Decode(qcoeff_P, diff, QP_xmitted, Mode); recon_data_P = MB_Recon_P(prev_recon, prev_ipol,diff, i*MB_SIZE,j*MB_SIZE,MV,pic->PB); Clip(recon_data_P); free(diff); /* 利用P帧宏块和已解码图像来预测B帧宏块 */ if (pic->PB) { diff = Predict_B(B_image, prev_recon, prev_ipol,i*MB_SIZE, j*MB_SIZE, MV, recon_data_P, frameskip, pic->TRB); if (QP_xmitted == 0) QP_B = 0; /* QP = 0 表示不进行量化 */ else QP_B = mmax(1,mmin(31,bquant[pic->BQUANT]*QP_xmitted/4)); qcoeff_B = MB_Encode(diff, QP_B, MODE_INTER); CBPB = FindCBP(qcoeff_B, MODE_INTER, 64); if (CBPB) MB_Decode(qcoeff_B, diff, QP_B, MODE_INTER); else ZeroMBlock(diff); recon_data_B = MB_Recon_B(prev_recon, diff,prev_ipol,i*MB_SIZE, j*MB_SIZE,MV,recon_data_P,frameskip, pic->TRB); Clip(recon_data_B); /* 判定MODB类型 */ if (CBPB) { pic->MODB = PBMODE_CBPB_MVDB; } else { if (MV[5][j+1][i+1]->x == 0 && MV[5][j+1][i+1]->y == 0) pic->MODB = PBMODE_NORMAL; else pic->MODB = PBMODE_MVDB; } free(diff); } else ZeroVec(MV[5][j+1][i+1]); /* Zero out PB deltas */ if ((CBP==0) && (CBPB==0) && (EqualVec(MV[0][j+1][i+1],&ZERO)) && (EqualVec(MV[5][j+1][i+1],&ZERO)) && (Mode == MODE_INTER || Mode == MODE_INTER_Q)) { /* 跳过宏块:CBP和CBPB都等于0,16×16的运动向量也为0。 如果PB帧模式下的运动向量变化为0,那么编码模式为帧间模式 */ if (Mode == MODE_INTER_Q) { /* DQUANT != 0 but not coded anyway */ QP_xmitted = QP_prev; pic->DQUANT = 0; Mode = MODE_INTER; } if (!syntax_arith_coding) CountBitsMB(Mode,1,CBP,CBPB,pic,bits); else Count_sac_BitsMB(Mode,1,CBP,CBPB,pic,bits); } else { /* 常规的宏块 */ if (!syntax_arith_coding) { /* 变长编码 */ CountBitsMB(Mode,0,CBP,CBPB,pic,bits); if (Mode == MODE_INTER || Mode == MODE_INTER_Q) { bits->no_inter++; CountBitsVectors(MV, bits, i, j, Mode, newgob, pic); } else if (Mode == MODE_INTER4V) { bits->no_inter4v++; CountBitsVectors(MV, bits, i, j, Mode, newgob, pic); } else { /* 帧内模式或帧内量化模式 */ bits->no_intra++; if (pic->PB) CountBitsVectors(MV, bits, i, j, Mode, newgob, pic); } if (CBP || Mode == MODE_INTRA || Mode == MODE_INTRA_Q) CountBitsCoeff(qcoeff_P, Mode, CBP, bits, 64); if (CBPB) CountBitsCoeff(qcoeff_B, MODE_INTER, CBPB, bits, 64); } /* 变长编码结束 */ else { /* SAC编码 */ Count_sac_BitsMB(Mode,0,CBP,CBPB,pic,bits); if (Mode == MODE_INTER || Mode == MODE_INTER_Q) { bits->no_inter++; Count_sac_BitsVectors(MV, bits, i, j, Mode, newgob, pic); } else if (Mode == MODE_INTER4V) { bits->no_inter4v++; Count_sac_BitsVectors(MV, bits, i, j, Mode, newgob, pic); } else { /* 帧内模式或帧内量化模式 */ bits->no_intra++; if (pic->PB) Count_sac_BitsVectors(MV, bits, i, j, Mode, newgob, pic); } if (CBP || Mode == MODE_INTRA || Mode == MODE_INTRA_Q) Count_sac_BitsCoeff(qcoeff_P, Mode, CBP, bits, 64); if (CBPB) Count_sac_BitsCoeff(qcoeff_B, MODE_INTER, CBPB, bits, 64); } /* SAC编码结束 */ QP_prev = QP_xmitted; } abs_mb_num++; QP_cumulative += QP_xmitted; #ifdef PRINTQ /* 若量化器在帧内发生变换 */ if (QuantChangePostponed) fprintf(stdout,"@%2d",QP_xmitted); else fprintf(stdout," %2d",QP_xmitted);#endif if (pic->PB) ReconImage(i,j,recon_data_B,B_recon); ReconImage(i,j,recon_data_P,recon); free(recon_data_P); free(qcoeff_P); if (pic->PB) { free(qcoeff_B); free(recon_data_B); } }#ifdef PRINTQ fprintf(stdout,"\n");#endif } pic->QP_mean = QP_cumulative/(float)abs_mb_num; /* 释放内存 */ free(pi); if (mv_outside_frame) { free(prev_recon); FreeImage(pr_edge); } for (j = 0; j < (lines>>4)+1; j++) for (i = 0; i < (pels>>4)+2; i++) for (k = 0; k < 6; k++) free(MV[k][j][i]); return;}/********************************************************************** * * 函数名: CodeOneIntra * 函数功能: 编码一帧I帧图像 * 函数输入: I帧图像数据指针及量化参数 * 函数返回值:重构图像的数据指针 ***********************************************************************/PictImage *CodeOneIntra(PictImage *curr, int QP, Bits *bits, Pict *pic)
{
PictImage *recon; //函数返回值,一个重构的视频帧
MB_Structure *data = (MB_Structure *)malloc(sizeof(MB_Structure));
int *qcoeff;
int Mode = MODE_INTRA;
int CBP,COD;
int i,j;
//初始化
recon = InitImage(pels*lines);
ZeroBits(bits);
//设定量化参数
pic->QUANT = QP;
bits->header += CountBitsPicture(pic);
COD = 0; /* 所有的编码块都在帧内编码*/
for ( j = 0; j < lines/MB_SIZE; j++) {
/* 在每一个帧图像分片中插入数据流语义信息 */
if (pic->use_gobsync && j != 0)
bits->header += CountBitsSlice(j,QP);
for ( i = 0; i < pels/MB_SIZE; i++) {
//将帧数据组成宏块和编码块
pic->MB = i + j * (pels/MB_SIZE);
bits->no_intra++;
FillLumBlock(i*MB_SIZE, j*MB_SIZE, curr, data);
FillChromBlock(i*MB_SIZE, j*MB_SIZE, curr, data);
qcoeff = MB_Encode(data, QP, Mode);
CBP = FindCBP(qcoeff,Mode,64);
//使用VLC编码
if (!syntax_arith_coding) {
CountBitsMB(Mode,COD,CBP,0,pic,bits);
CountBitsCoeff(qcoeff, Mode, CBP,bits,64);
} else { //使用SAC编码
Count_sac_BitsMB(Mode,COD,CBP,0,pic,bits);
Count_sac_BitsCoeff(qcoeff, Mode, CBP,bits,64);
}
//重构视频帧,确定输出
MB_Decode(qcoeff, data, QP, Mode);
Clip(data);
ReconImage(i,j,data,recon);
free(qcoeff);
}
}
pic->QP_mean = (float)QP;
free(data);
return recon;
}
/********************************************************************** * * 函数名: MB_Encode * 函数功能:宏块的DCT变换与量化 * * 输入: 宏块数据结构,量化参数,宏块信息结构
* 返回值:量化系数的指针 **********************************************************************/int *MB_Encode(MB_Structure *mb_orig, int QP, int I){ int i, j, k, l, row, col; int fblock[64]; int coeff[384]; int *coeff_ind; int *qcoeff; int *qcoeff_ind; if ((qcoeff=(int *)malloc(sizeof(int)*384)) == 0) { fprintf(stderr,"mb_encode(): Couldn't allocate qcoeff.\n"); exit(-1); } coeff_ind = coeff; qcoeff_ind = qcoeff; for (k=0;k<16;k+=8) { for (l=0;l<16;l+=8) { for (i=k,row=0;row<64;i++,row+=8) { for (j=l,col=0;col<8;j++,col++) { *(fblock+row+col) = mb_orig->lum[i][j]; } } Dct(fblock,coeff_ind); Quant(coeff_ind,qcoeff_ind,QP,I); coeff_ind += 64; qcoeff_ind += 64; } } for (i=0;i<8;i++) { for (j=0;j<8;j++) { *(fblock+i*8+j) = mb_orig->Cb[i][j]; } } Dct(fblock,coeff_ind); Quant(coeff_ind,qcoeff_ind,QP,I); coeff_ind += 64; qcoeff_ind += 64; for (i=0;i<8;i++) { for (j=0;j<8;j++) { *(fblock+i*8+j) = mb_orig->Cr[i][j]; } } Dct(fblock,coeff_ind); Quant(coeff_ind,qcoeff_ind,QP,I); return qcoeff;}/********************************************************************** * * 函数名: MB_Decode * 函数功能: 重构一个量化后DCT编码的宏块 * 输入:量化系数、编码宏块、量化参数以及宏块信息 * 返回值:0 * **********************************************************************/ int MB_Decode(int *qcoeff, MB_Structure *mb_recon, int QP, int I){ int i, j, k, l, row, col; int *iblock; int *qcoeff_ind; int *rcoeff, *rcoeff_ind; if ((iblock = (int *)malloc(sizeof(int)*64)) == NULL) { fprintf(stderr,"MB_Coder: Could not allocate space for iblock\n");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -