JM ldecod分析:解码slice
承接上节 JM ldecod分析
read_new_slice()从h264数据流中获取一个slice。
slice结构参见关键数据结构
(后边提到的数据结构均在该链接中)
NALU_t结构是nalu的描述头,用来保存nalu的头信息和nalu body的buf指针。
read_new_slice
从bitstream中读取到slice之后,slice分为slice_header和slice_data两部分。读取slice_header 的部分字段的时候,需要首先获取pps 和sps。所以读取slice_header时,代码分成两部分。slice_header字段使用的是哥伦布编码进行编码的。
slice_data字段主要使用熵编码,CAVLC和CABAC。
/*!
************************************************************************
* \brief
* Reads new slice from bit_stream_dec
************************************************************************
*/
int read_new_slice(Slice *currSlice)
{
VideoParameters *p_Vid = currSlice->p_Vid;
InputParameters *p_Inp = currSlice->p_Inp;
NALU_t *nalu = p_Vid->nalu;
int current_header = 0;
int BitsUsedByHeader;
Bitstream *currStream = NULL;
static NALU_t *pending_nalu = NULL;
int slice_id_a, slice_id_b, slice_id_c;
for (;;)
{
#if (MVC_EXTENSION_ENABLE)
currSlice->svc_extension_flag = -1;
#endif
if (!pending_nalu)
{
读取一个nalu(ebsp已经被转化为rbsp)
if (0 == read_next_nalu(p_Vid, nalu))
return EOS;
}
else
{
nalu = pending_nalu;
pending_nalu = NULL;
}
#if (MVC_EXTENSION_ENABLE)
if(p_Inp->DecodeAllLayers == 1 && (nalu->nal_unit_type == NALU_TYPE_PREFIX || nalu->nal_unit_type == NALU_TYPE_SLC_EXT))
{
currStream = currSlice->partArr[0].bitstream;
currStream->ei_flag = 0;
currStream->frame_bitoffset = currStream->read_len = 0;
fast_memcpy (currStream->streamBuffer, &nalu->buf[1], nalu->len-1);
currStream->code_len = currStream->bitstream_length = RBSPtoSODB(currStream->streamBuffer, nalu->len-1);
currSlice->svc_extension_flag = read_u_1 ("svc_extension_flag" , currStream, &p_Dec->UsedBits);
if(currSlice->svc_extension_flag)
{
nal_unit_header_svc_extension();
}
else
{
nal_unit_header_mvc_extension(&currSlice->NaluHeaderMVCExt, currStream);
currSlice->NaluHeaderMVCExt.iPrefixNALU = (nalu->nal_unit_type == NALU_TYPE_PREFIX);
}
if(nalu->nal_unit_type == NALU_TYPE_SLC_EXT)
{
if(currSlice->svc_extension_flag)
{
//to be implemented for Annex G;
}
else
{
nalu->nal_unit_type = NALU_TYPE_SLICE; //currSlice->NaluHeaderMVCExt.non_idr_flag==0? NALU_TYPE_IDR: NALU_TYPE_SLICE;
}
}
}
#endif
根据nalu类型进行处理
process_nalu:
switch (nalu->nal_unit_type)
{
case NALU_TYPE_SLICE:
case NALU_TYPE_IDR:
if (p_Vid->recovery_point || nalu->nal_unit_type == NALU_TYPE_IDR)
{
if (p_Vid->recovery_point_found == 0)
{
if (nalu->nal_unit_type != NALU_TYPE_IDR)
{
printf("Warning: Decoding does not start with an IDR picture.\n");
p_Vid->non_conforming_stream = 1;
}
else
p_Vid->non_conforming_stream = 0;
}
p_Vid->recovery_point_found = 1;
}
if (p_Vid->recovery_point_found == 0)
break;
currSlice->idr_flag = (nalu->nal_unit_type == NALU_TYPE_IDR);
currSlice->nal_reference_idc = nalu->nal_reference_idc;
currSlice->dp_mode = PAR_DP_1;
currSlice->max_part_nr = 1;
#if (MVC_EXTENSION_ENABLE)
if (currSlice->svc_extension_flag != 0)
{
currStream = currSlice->partArr[0].bitstream;
currStream->ei_flag = 0;
currStream->frame_bitoffset = currStream->read_len = 0;
fast_memcpy (currStream->streamBuffer, &nalu->buf[1], nalu->len-1);
currStream->code_len = currStream->bitstream_length = RBSPtoSODB(currStream->streamBuffer, nalu->len-1);
}
#else
currStream = currSlice->partArr[0].bitstream;
currStream->ei_flag = 0;
currStream->frame_bitoffset = currStream->read_len = 0;
保存解码原数据
memcpy (currStream->streamBuffer, &nalu->buf[1], nalu->len-1);
将最后的填充0 bit删掉,获得code_len 编码后数据长度
currStream->code_len = currStream->bitstream_length = RBSPtoSODB(currStream->streamBuffer, nalu->len-1);
#endif
#if (MVC_EXTENSION_ENABLE)
if(currSlice->svc_extension_flag == 0)
{ //MVC
//if(is_MVC_profile(p_Vid->active_sps->profile_idc))
//{
currSlice->view_id = currSlice->NaluHeaderMVCExt.view_id;
currSlice->inter_view_flag = currSlice->NaluHeaderMVCExt.inter_view_flag;
currSlice->anchor_pic_flag = currSlice->NaluHeaderMVCExt.anchor_pic_flag;
//}
}
else if(currSlice->svc_extension_flag == -1) //SVC and the normal AVC;
{
if(p_Vid->active_subset_sps == NULL)
{
currSlice->view_id = GetBaseViewId(p_Vid, &p_Vid->active_subset_sps);
if(currSlice->NaluHeaderMVCExt.iPrefixNALU >0)
{
assert(currSlice->view_id == currSlice->NaluHeaderMVCExt.view_id);
currSlice->inter_view_flag = currSlice->NaluHeaderMVCExt.inter_view_flag;
currSlice->anchor_pic_flag = currSlice->NaluHeaderMVCExt.anchor_pic_flag;
}
else
{
currSlice->inter_view_flag = 1;
currSlice->anchor_pic_flag = currSlice->idr_flag;
}
}
else
{
assert(p_Vid->active_subset_sps->num_views_minus1 >=0);
// prefix NALU available
if(currSlice->NaluHeaderMVCExt.iPrefixNALU >0)
{
currSlice->view_id = currSlice->NaluHeaderMVCExt.view_id;
currSlice->inter_view_flag = currSlice->NaluHeaderMVCExt.inter_view_flag;
currSlice->anchor_pic_flag = currSlice->NaluHeaderMVCExt.anchor_pic_flag;
}
else
{ //no prefix NALU;
currSlice->view_id = p_Vid->active_subset_sps->view_id[0];
currSlice->inter_view_flag = 1;
currSlice->anchor_pic_flag = currSlice->idr_flag;
}
}
}
currSlice->layer_id = currSlice->view_id = GetVOIdx( p_Vid, currSlice->view_id );
#endif
// Some syntax of the Slice Header depends on the parameter set, which depends on
// the parameter set ID of the SLice header. Hence, read the pic_parameter_set_id
// of the slice header first, then setup the active parameter sets, and then read
// the rest of the slice header
BitsUsedByHeader = FirstPartOfSliceHeader(currSlice);
UseParameterSet (currSlice);
currSlice->active_sps = p_Vid->active_sps;
currSlice->active_pps = p_Vid->active_pps;
currSlice->Transform8x8Mode = p_Vid->active_pps->transform_8x8_mode_flag;
currSlice->chroma444_not_separate = (p_Vid->active_sps->chroma_format_idc==YUV444)&&((p_Vid->separate_colour_plane_flag == 0));
BitsUsedByHeader += RestOfSliceHeader (currSlice);
#if (MVC_EXTENSION_ENABLE)
if(currSlice->view_id >=0)
{
currSlice->p_Dpb = p_Vid->p_Dpb_layer[currSlice->view_id];
}
#endif
assign_quant_params (currSlice);
// if primary slice is replaced with redundant slice, set the correct image type
if(currSlice->redundant_pic_cnt && p_Vid->Is_primary_correct==0 && p_Vid->Is_redundant_correct)
{
p_Vid->dec_picture->slice_type = p_Vid->type;
}
if(is_new_picture(p_Vid->dec_picture, currSlice, p_Vid->old_slice))
{
if(p_Vid->iSliceNumOfCurrPic==0)
init_picture(p_Vid, currSlice, p_Inp);
current_header = SOP;
//check zero_byte if it is also the first NAL unit in the access unit
CheckZeroByteVCL(p_Vid, nalu);
}
else
current_header = SOS;
setup_slice_methods(currSlice);
// From here on, p_Vid->active_sps, p_Vid->active_pps and the slice header are valid
if (currSlice->mb_aff_frame_flag)
currSlice->current_mb_nr = currSlice->start_mb_nr << 1;
else
currSlice->current_mb_nr = currSlice->start_mb_nr;
if (p_Vid->active_pps->entropy_coding_mode_flag)
{
int ByteStartPosition = currStream->frame_bitoffset/8;
if (currStream->frame_bitoffset%8 != 0)
{
++ByteStartPosition;
}
arideco_start_decoding (&currSlice->partArr[0].de_cabac, currStream->streamBuffer, ByteStartPosition, &currStream->read_len);
}
// printf ("read_new_slice: returning %s\n", current_header == SOP?"SOP":"SOS");
//FreeNALU(nalu);
p_Vid->recovery_point = 0;
return current_header;
break;
case NALU_TYPE_DPA:
if (p_Vid->recovery_point_found == 0)
break;
// read DP_A
currSlice->dpB_NotPresent =1;
currSlice->dpC_NotPresent =1;
currSlice->idr_flag = FALSE;
currSlice->nal_reference_idc = nalu->nal_reference_idc;
currSlice->dp_mode = PAR_DP_3;
currSlice->max_part_nr = 3;
currSlice->ei_flag = 0;
#if MVC_EXTENSION_ENABLE
currSlice->p_Dpb = p_Vid->p_Dpb_layer[0];
#endif
currStream = currSlice->partArr[0].bitstream;
currStream->ei_flag = 0;
currStream->frame_bitoffset = currStream->read_len = 0;
memcpy (currStream->streamBuffer, &nalu->buf[1], nalu->len-1);
currStream->code_len = currStream->bitstream_length = RBSPtoSODB(currStream->streamBuffer, nalu->len-1);
#if MVC_EXTENSION_ENABLE
currSlice->view_id = GetBaseViewId(p_Vid, &p_Vid->active_subset_sps);
currSlice->inter_view_flag = 1;
currSlice->layer_id = currSlice->view_id = GetVOIdx( p_Vid, currSlice->view_id );
currSlice->anchor_pic_flag = currSlice->idr_flag;
#endif
BitsUsedByHeader = FirstPartOfSliceHeader(currSlice);
UseParameterSet (currSlice);
currSlice->active_sps = p_Vid->active_sps;
currSlice->active_pps = p_Vid->active_pps;
currSlice->Transform8x8Mode = p_Vid->active_pps->transform_8x8_mode_flag;
currSlice->chroma444_not_separate = (p_Vid->active_sps->chroma_format_idc==YUV444)&&((p_Vid->separate_colour_plane_flag == 0));
BitsUsedByHeader += RestOfSliceHeader (currSlice);
#if MVC_EXTENSION_ENABLE
currSlice->p_Dpb = p_Vid->p_Dpb_layer[currSlice->view_id];
#endif
assign_quant_params (currSlice);
if(is_new_picture(p_Vid->dec_picture, currSlice, p_Vid->old_slice))
{
if(p_Vid->iSliceNumOfCurrPic==0)
init_picture(p_Vid, currSlice, p_Inp);
current_header = SOP;
//check zero_byte if it is also the first NAL unit in the access unit
CheckZeroByteVCL(p_Vid, nalu);
}
else
current_header = SOS;
setup_slice_methods(currSlice);
// From here on, p_Vid->active_sps, p_Vid->active_pps and the slice header are valid
if (currSlice->mb_aff_frame_flag)
currSlice->current_mb_nr = currSlice->start_mb_nr << 1;
else
currSlice->current_mb_nr = currSlice->start_mb_nr;
// Now I need to read the slice ID, which depends on the value of
// redundant_pic_cnt_present_flag
slice_id_a = read_ue_v("NALU: DP_A slice_id", currStream, &p_Dec->UsedBits);
if (p_Vid->active_pps->entropy_coding_mode_flag)
error ("received data partition with CABAC, this is not allowed", 500);
// continue with reading next DP
if (0 == read_next_nalu(p_Vid, nalu))
return current_header;
if ( NALU_TYPE_DPB == nalu->nal_unit_type)
{
// we got a DPB
currStream = currSlice->partArr[1].bitstream;
currStream->ei_flag = 0;
currStream->frame_bitoffset = currStream->read_len = 0;
memcpy (currStream->streamBuffer, &nalu->buf[1], nalu->len-1);
currStream->code_len = currStream->bitstream_length = RBSPtoSODB(currStream->streamBuffer, nalu->len-1);
slice_id_b = read_ue_v("NALU: DP_B slice_id", currStream, &p_Dec->UsedBits);
currSlice->dpB_NotPresent = 0;
if ((slice_id_b != slice_id_a) || (nalu->lost_packets))
{
printf ("Waning: got a data partition B which does not match DP_A (DP loss!)\n");
currSlice->dpB_NotPresent =1;
currSlice->dpC_NotPresent =1;
}
else
{
if (p_Vid->active_pps->redundant_pic_cnt_present_flag)
read_ue_v("NALU: DP_B redundant_pic_cnt", currStream, &p_Dec->UsedBits);
// we're finished with DP_B, so let's continue with next DP
if (0 == read_next_nalu(p_Vid, nalu))
return current_header;
}
}
else
{
currSlice->dpB_NotPresent =1;
}
// check if we got DP_C
if ( NALU_TYPE_DPC == nalu->nal_unit_type)
{
currStream = currSlice->partArr[2].bitstream;
currStream->ei_flag = 0;
currStream->frame_bitoffset = currStream->read_len = 0;
memcpy (currStream->streamBuffer, &nalu->buf[1], nalu->len-1);
currStream->code_len = currStream->bitstream_length = RBSPtoSODB(currStream->streamBuffer, nalu->len-1);
currSlice->dpC_NotPresent = 0;
slice_id_c = read_ue_v("NALU: DP_C slice_id", currStream, &p_Dec->UsedBits);
if ((slice_id_c != slice_id_a)|| (nalu->lost_packets))
{
printf ("Warning: got a data partition C which does not match DP_A(DP loss!)\n");
//currSlice->dpB_NotPresent =1;
currSlice->dpC_NotPresent =1;
}
if (p_Vid->active_pps->redundant_pic_cnt_present_flag)
read_ue_v("NALU:SLICE_C redudand_pic_cnt", currStream, &p_Dec->UsedBits);
}
else
{
currSlice->dpC_NotPresent =1;
pending_nalu = nalu;
}
// check if we read anything else than the expected partitions
if ((nalu->nal_unit_type != NALU_TYPE_DPB) && (nalu->nal_unit_type != NALU_TYPE_DPC) && (!currSlice->dpC_NotPresent))
{
// we have a NALI that we can't process here, so restart processing
goto process_nalu;
// yes, "goto" should not be used, but it's really the best way here before we restructure the decoding loop
// (which should be taken care of anyway)
}
//FreeNALU(nalu);
return current_header;
break;
case NALU_TYPE_DPB:
if (p_Inp->silent == FALSE)
{
printf ("found data partition B without matching DP A, discarding\n");
}
break;
case NALU_TYPE_DPC:
if (p_Inp->silent == FALSE)
{
printf ("found data partition C without matching DP A, discarding\n");
}
break;
case NALU_TYPE_SEI:
//printf ("read_new_slice: Found NALU_TYPE_SEI, len %d\n", nalu->len);
InterpretSEIMessage(nalu->buf,nalu->len,p_Vid, currSlice);
break;
case NALU_TYPE_PPS:
//printf ("Found NALU_TYPE_PPS\n");
ProcessPPS(p_Vid, nalu);
break;
case NALU_TYPE_SPS:
//printf ("Found NALU_TYPE_SPS\n");
ProcessSPS(p_Vid, nalu);
break;
case NALU_TYPE_AUD:
//printf ("Found NALU_TYPE_AUD\n");
// printf ("read_new_slice: Found 'Access Unit Delimiter' NAL unit, len %d, ignored\n", nalu->len);
break;
case NALU_TYPE_EOSEQ:
// printf ("read_new_slice: Found 'End of Sequence' NAL unit, len %d, ignored\n", nalu->len);
break;
case NALU_TYPE_EOSTREAM:
// printf ("read_new_slice: Found 'End of Stream' NAL unit, len %d, ignored\n", nalu->len);
break;
case NALU_TYPE_FILL:
if (p_Inp->silent == FALSE)
{
//printf ("read_new_slice: Found NALU_TYPE_FILL, len %d\n", (int) nalu->len);
//printf ("Skipping these filling bits, proceeding w/ next NALU\n");
}
break;
#if (MVC_EXTENSION_ENABLE)
case NALU_TYPE_VDRD:
//printf ("Found NALU_TYPE_VDRD\n");
// printf ("read_new_slice: Found 'View and Dependency Representation Delimiter' NAL unit, len %d, ignored\n", nalu->len);
break;
case NALU_TYPE_PREFIX:
//printf ("Found NALU_TYPE_PREFIX\n");
if(currSlice->svc_extension_flag==1)
prefix_nal_unit_svc();
break;
case NALU_TYPE_SUB_SPS:
//printf ("Found NALU_TYPE_SUB_SPS\n");
if (p_Inp->DecodeAllLayers== 1)
{
ProcessSubsetSPS(p_Vid, nalu);
}
else
{
if (p_Inp->silent == FALSE)
printf ("Found Subsequence SPS NALU. Ignoring.\n");
}
break;
case NALU_TYPE_SLC_EXT:
//printf ("Found NALU_TYPE_SLC_EXT\n");
if (p_Inp->DecodeAllLayers == 0 && (p_Inp->silent == FALSE))
printf ("Found SVC extension NALU (%d). Ignoring.\n", (int) nalu->nal_unit_type);
break;
#endif
default:
{
if (p_Inp->silent == FALSE)
printf ("Found NALU type %d, len %d undefined, ignore NALU, moving on\n", (int) nalu->nal_unit_type, (int) nalu->len);
}
break;
}
}
}
read_new_slice() 中首先分割bit流通过read_next_nalu()获取到下一个Nalu,根据Nalu的类型进行处理: