2)解码一帧图像
MPEG-4 SP视频编码算法中,编码帧类型是I帧和P帧。码流头结构中有帧类型信息、量化信息、fcode值、intra_dc_threshold等。XviD提供的MPEG-4视频解码在解码到VOL时会返回应用程序,为了应用层更直接地得到解码图像,现在修改为只有解析并解码了图像帧数据时才返回应用程序。
解码器解码图像decoder_decode()的流程如图14-7所示。根据该流程,代码实现如下。
int decoder_decode(DECODER * dec, xvid_dec_frame_t *
frame, xvid_dec_stats_t * stats)
{
uint32_t rounding; /*图像饱和
类型,在插值时四舍五入的饱和大小*/
uint32_t reduced_resolution;
uint32_t quant = 2; /*默认的量化步长初始化*/
uint32_t fcode_forward; /*运动估计中搜索窗口的大小,前向*/
uint32_t fcode_backward; /*运动估计中搜索窗口的大小,后向*/
uint32_t intra_dc_threshold; /*Intra块的直流DC阈值*/
WARPPOINTS gmc_warp;
int coding_type; /*当前编码帧类型*/
if (frame->length < 0) return
XVID_ERR_END; /*检测输入码流长度的合法性*/
BitstreamInit(&g_bs, frame->bitstream,
frame->length); /*初始化码流结构体*/
/*解析视频码流头结构、信息*/
repeat:
coding_type = BitstreamReadHeaders(&g_bs, dec,
&rounding, &reduced_resolution,
&quant, &fcode_forward, &fcode_backward,
&intra_dc_threshold, &gmc_warp);
if (coding_type == -1) { /* 继续解析头信息,直到解析到图像数据 */
goto repeat;
}
if (coding_type == -2 || coding_type == -3) {/*
当前是VOL头或需要修改图像空间大小 */
if (coding_type == -3) decoder_resize(dec);
if (stats) goto repeat; /* 继续解析头信息,直到解析到图像数据 */
}
/*确保解码的第一帧是I帧,如果不是I帧则继续解析后续码流*/
if(dec->frames == 0 && coding_type != I_VOP) goto repeat;
if (coding_type != B_VOP) {
switch(coding_type) {
case I_VOP : /*I帧解码*/
decoder_iframe(dec, &g_bs, reduced_resolution,
quant, intra_dc_ threshold);
break;
case P_VOP : /*P帧解码*/
decoder_pframe(dec, &g_bs, rounding, reduced_resolution, quant,
fcode_forward, intra_dc_threshold, NULL);
break;
case S_VOP : /*S帧解码*/
decoder_pframe(dec, &g_bs, rounding, reduced_resolution, quant,
fcode_forward, intra_dc_threshold, &gmc_warp);
break;
}
dec_image_swap(&dec->refn[0], &dec->refn[1]);
/*交换两个参考帧的指针*/
dec_image_swap(&dec->cur, &dec->refn[0]);
/*交换当前图像和参考帧的指针*/
dec->last_reduced_resolution = reduced_resolution;
dec->last_coding_type = coding_type; /*保存当前帧类型*/
dec->frames++; /*解码帧计数器*/
}
done :
return (BitstreamPos(&g_bs) + 7) / 8;
/*已解码的码流,单位B*/
}
图14-7 decoder_decode()流程图BitstreamReadHeaders()函数根据MPEG-4标准码流协议解析编码信息。如果用户在编码端对VOL、VOP的写头信息作了修改,则在解码端的该函数中,要一一对应起来。如为了加密需要,就可以在编码端的写VOL/VOP信息作特定的修改。SP档级的MPEG-4视频帧有I帧和P帧,本章对I帧和P帧的解码过程做剖析。I帧解码由decoder_iframe函数、P帧解码由decoder_pframe函数完成。
① decoder_iframe
解码是其编码的逆工作,获得mcbpc、acpred_flag、cbpy等,将运动向量初始化为0。
static void decoder_iframe(DECODER * dec,
Bitstream * bs, int quant, int intra_dc_threshold)
{
uint32_t x, y;
const uint32_t mb_width = dec->mb_width;
const uint32_t mb_height = dec->mb_height;
/*循环解码所有宏块*/
for (y = 0; y < mb_height; y++)
for (x = 0; x < mb_width; x++) {
MACROBLOCK *mb;
uint32_t mcbpc, cbpc, acpred_flag, cbpy, cbp;
mb = &dec->mbs[y * dec->mb_width + x];
mcbpc = get_mcbpc_intra(bs); /*解码得到mcbpc*/
mb->mode = mcbpc & 7; /*宏块编码模式*/
cbpc = (mcbpc >> 4); /*组合得到cbpc*/
acpred_flag = BitstreamGetBit(bs); /*AC预测方向*/
cbpy = get_cbpy(bs, 1); /*解码得到cbpy*/
cbp = (cbpy << 2) | cbpc; /*组合得到cbp*/
mb->quant = quant;
mb->mvs[0].x = mb->mvs[0].y = mb->mvs[1].x = mb->mvs[1].y =
mb->mvs[2].x = mb->mvs[2].y = mb->mvs[3].x = mb->mvs[3].y =0;
decoder_mbintra(dec, mb, x, y, acpred_flag,
cbp, bs, quant, intra_dc_ threshold, 0);
}
}
上述代码实现I帧图像的解码,首先从码流中获取mcbpc、AC预测方向、cbpy,将运动量清零,调用核心函数decoder_mbintra进行解码,以宏块为单位循环处理。