Something about Exp-Golomb codes in H.264 sps
When I was parsing the sps information of H264, I found some special symbol like u(1), ue(v), se(v), it's an Exp-Golomb codes, so what is Exp-Golomb codes? You can refer to this link:http://en.wikipedia.org/wiki/Exponential-Golomb_coding, and now I'm going to tell you how to parse the codes.
In the H.264 Standerd documentation, it says that Syntax elements coded as ue(v), me(v), or se(v) are Exp-Golomb-coded. Syntax elements coded as te(v) are truncated Exp-Golomb-coded. The parsing process for these syntax elements begins with reading the bits starting at the current location in the bitstream up to and including the first non-zero bit, and counting the number of leading bits that are equal to 0. The process is specified as follows:
leadingZeroBits = -1; for(b = 0; !b; leadingZeroBits ++) b = read_bits(1);
The variable codeNum is then assigned as follows:
codeNum = 2^leadingZeroBits - 1 + read_bits(leadingZeroBits);
So we can get the codeNum like this:
All these information comes from ITU-T Rec.H.264(03/2005), up is a sample used as ue(v). And now I'm telling you how to use this code to parse the sps information by java code.
We can see the sps struct is like this:
Here is the parse function:
// Parameters: // data - pointer to NAL unit // len - NAL unit length // frmDimension - LOW WORD: frame width; HIGH WORD: frame height // Note: the NAL unit type should be NALU_TYPE_SEQUENCE_PARAMETER_SET(7) public boolean NAL_retrieveFrameDimension(byte data[], int offset, int len) { //byte pData[] = data; //int dataLen = len; byte [] pData = new byte[len]; System.arraycopy(data, offset, pData, 0, len ); int dataLen = len; // filter RBSP for slice_header:slice_type parsing //byte rbspBuf[] = new byte[SEQUENCE_PARAMETER_SET_TEMP_BUF_LENGTH]; byte [] rbspBuf = new byte[SEQUENCE_PARAMETER_SET_TEMP_BUF_LENGTH]; int rbspByteNum = 0; // let's extract first SEQUENCE_PARAMETER_SET_TEMP_BUF_LENGTH(768) bytes // in RBSP // for slice_type determination for (int i = 1; i < dataLen; i++) // index i start from 1: we should // skip the NAL heade byte { // Don't take emulation_prevention_three_byte(0x03) into account if ((i + 2) < dataLen && ((pData[i + 0] == 0) && (pData[i + 1] == 0) && (pData[i + 2] == 3))) { if (rbspByteNum < SEQUENCE_PARAMETER_SET_TEMP_BUF_LENGTH) rbspBuf[rbspByteNum++] = pData[i]; if (rbspByteNum < SEQUENCE_PARAMETER_SET_TEMP_BUF_LENGTH) rbspBuf[rbspByteNum++] = pData[i + 1]; i += 2; } else { if (rbspByteNum < SEQUENCE_PARAMETER_SET_TEMP_BUF_LENGTH) rbspBuf[rbspByteNum++] = pData[i]; } if (rbspByteNum >= SEQUENCE_PARAMETER_SET_TEMP_BUF_LENGTH) break; } currentOperatedOffset = 0; int leadingZeroBits = 0; int val = 0; // get profile id m_profileID = (short) nextBits(rbspBuf, currentOperatedOffset, 8, SEQUENCE_PARAMETER_SET_TEMP_BUF_LENGTH); // skip constraint flags and reserved zero bites System.out.println("profile_idc====>" + m_profileID); currentOperatedOffset += 8; // get level id m_levelID = (short) nextBits(rbspBuf, currentOperatedOffset, 8, SEQUENCE_PARAMETER_SET_TEMP_BUF_LENGTH); System.out.println("leved_idc====>" + m_levelID); // retrieve seq_parameter_set_id while (nextBits(rbspBuf, currentOperatedOffset, 1, SEQUENCE_PARAMETER_SET_TEMP_BUF_LENGTH) == 0 && leadingZeroBits < 32) leadingZeroBits++; m_seqParamSetID = (short) ((1 << leadingZeroBits) - 1 + nextBits( rbspBuf, currentOperatedOffset, leadingZeroBits, SEQUENCE_PARAMETER_SET_TEMP_BUF_LENGTH)); System.out.println("seq_parameter_set_id====>" + m_seqParamSetID); // skip extra bits in case that the profile id is 100/110/122/144 if ((m_profileID == H264_PROFILE_ID_HIGH) || (m_profileID == H264_PROFILE_ID_HIGH10) || (m_profileID == H264_PROFILE_ID_HIGH422) || (m_profileID == H264_PROFILE_ID_HIGH444)) { // skip chroma_format_idc leadingZeroBits = 0; while (nextBits(rbspBuf, currentOperatedOffset, 1, SEQUENCE_PARAMETER_SET_TEMP_BUF_LENGTH) == 0 && leadingZeroBits < 32) leadingZeroBits++; val = (1 << leadingZeroBits) - 1 + nextBits(rbspBuf, currentOperatedOffset, leadingZeroBits, SEQUENCE_PARAMETER_SET_TEMP_BUF_LENGTH); if (val == 3) currentOperatedOffset++; // skip residual_colour_transform_flag // skip bit_depth_luma_minus8 leadingZeroBits = 0; while (nextBits(rbspBuf, currentOperatedOffset, 1, SEQUENCE_PARAMETER_SET_TEMP_BUF_LENGTH) == 0 && leadingZeroBits < 32) leadingZeroBits++; nextBits(rbspBuf, currentOperatedOffset, leadingZeroBits, SEQUENCE_PARAMETER_SET_TEMP_BUF_LENGTH); // skip bit_depth_chroma_minus8 leadingZeroBits = 0; while (nextBits(rbspBuf, currentOperatedOffset, 1, SEQUENCE_PARAMETER_SET_TEMP_BUF_LENGTH) == 0 && leadingZeroBits < 32) leadingZeroBits++; nextBits(rbspBuf, currentOperatedOffset, leadingZeroBits, SEQUENCE_PARAMETER_SET_TEMP_BUF_LENGTH); // skip qpprime_y_zero_transform_bypass_flag currentOperatedOffset++; // get seq_scaling_matrix_present_flag boolean seq_scaling_matrix_present_flag = nextBits(rbspBuf, currentOperatedOffset, 1, SEQUENCE_PARAMETER_SET_TEMP_BUF_LENGTH) != 0; if (seq_scaling_matrix_present_flag) { for (int i = 0; i < 8; i++) { boolean seq_scaling_list_present_flag = nextBits(rbspBuf, currentOperatedOffset, 1, SEQUENCE_PARAMETER_SET_TEMP_BUF_LENGTH) != 0; if (seq_scaling_list_present_flag) { if (i < 6) SkipScalingList(16, rbspBuf, currentOperatedOffset); else SkipScalingList(64, rbspBuf, currentOperatedOffset); } } } } // retrieve m_log2_max_frame_num_minus4 leadingZeroBits = 0; while (nextBits(rbspBuf, currentOperatedOffset, 1, SEQUENCE_PARAMETER_SET_TEMP_BUF_LENGTH) == 0 && leadingZeroBits < 32) leadingZeroBits++; m_log2_max_frame_num_minus4 = (1 << leadingZeroBits) - 1 + nextBits(rbspBuf, currentOperatedOffset, leadingZeroBits, SEQUENCE_PARAMETER_SET_TEMP_BUF_LENGTH); // skip pic_order_cnt_type and associated data leadingZeroBits = 0; while (nextBits(rbspBuf, currentOperatedOffset, 1, SEQUENCE_PARAMETER_SET_TEMP_BUF_LENGTH) == 0 && leadingZeroBits < 32) leadingZeroBits++; val = (1 << leadingZeroBits) - 1 + nextBits(rbspBuf, currentOperatedOffset, leadingZeroBits, SEQUENCE_PARAMETER_SET_TEMP_BUF_LENGTH); if (val == 0) { // skip log2_max_pic_order_cnt_lsb_minus4 leadingZeroBits = 0; while (nextBits(rbspBuf, currentOperatedOffset, 1, SEQUENCE_PARAMETER_SET_TEMP_BUF_LENGTH) == 0 && leadingZeroBits < 32) leadingZeroBits++; nextBits(rbspBuf, currentOperatedOffset, leadingZeroBits, SEQUENCE_PARAMETER_SET_TEMP_BUF_LENGTH); } else if (val == 1) { // skip delta_pic_always_zero_flag currentOperatedOffset++; // skip offset_for_non_ref_pic leadingZeroBits = 0; while (nextBits(rbspBuf, currentOperatedOffset, 1, SEQUENCE_PARAMETER_SET_TEMP_BUF_LENGTH) == 0 && leadingZeroBits < 32) leadingZeroBits++; nextBits(rbspBuf, currentOperatedOffset, leadingZeroBits, SEQUENCE_PARAMETER_SET_TEMP_BUF_LENGTH); // skip offset_for_top_to_bottom_field leadingZeroBits = 0; while (nextBits(rbspBuf, currentOperatedOffset, 1, SEQUENCE_PARAMETER_SET_TEMP_BUF_LENGTH) == 0 && leadingZeroBits < 32) leadingZeroBits++; nextBits(rbspBuf, currentOperatedOffset, leadingZeroBits, SEQUENCE_PARAMETER_SET_TEMP_BUF_LENGTH); // retrieve num_ref_frames_in_pic_order_cnt_cycle leadingZeroBits = 0; while (nextBits(rbspBuf, currentOperatedOffset, 1, SEQUENCE_PARAMETER_SET_TEMP_BUF_LENGTH) == 0 && leadingZeroBits < 32) leadingZeroBits++; int num_ref_frames_in_pic_order_cnt_cycle = (1 << leadingZeroBits) - 1 + nextBits(rbspBuf, currentOperatedOffset, leadingZeroBits, SEQUENCE_PARAMETER_SET_TEMP_BUF_LENGTH); // skip offset_for_ref_frame[i] for (int i = 0; i < num_ref_frames_in_pic_order_cnt_cycle; i++) { leadingZeroBits = 0; while (nextBits(rbspBuf, currentOperatedOffset, 1, SEQUENCE_PARAMETER_SET_TEMP_BUF_LENGTH) == 0 && leadingZeroBits < 32) leadingZeroBits++; nextBits(rbspBuf, currentOperatedOffset, leadingZeroBits, SEQUENCE_PARAMETER_SET_TEMP_BUF_LENGTH); } } // skip num_ref_frames leadingZeroBits = 0; while (nextBits(rbspBuf, currentOperatedOffset, 1, SEQUENCE_PARAMETER_SET_TEMP_BUF_LENGTH) == 0 && leadingZeroBits < 32) leadingZeroBits++; nextBits(rbspBuf, currentOperatedOffset, leadingZeroBits, SEQUENCE_PARAMETER_SET_TEMP_BUF_LENGTH); // skip gaps_in_frame_num_value_allowed_flag currentOperatedOffset++; // retrieve pic_width_in_mbs_minus1 leadingZeroBits = 0; while (nextBits(rbspBuf, currentOperatedOffset, 1, SEQUENCE_PARAMETER_SET_TEMP_BUF_LENGTH) == 0 && leadingZeroBits < 128) leadingZeroBits++; val = (((int)(1L << leadingZeroBits)) & 0xFFFFFFFF) - 1 + nextBits(rbspBuf, currentOperatedOffset, leadingZeroBits, SEQUENCE_PARAMETER_SET_TEMP_BUF_LENGTH); m_picWidthSampleLuma = (short) ((val + 1) << 4); // PicWidthInSampleLuma // = // PicWidthInMbs*16 // retrieve pic_height_in_map_units_minus1 leadingZeroBits = 0; while (nextBits(rbspBuf, currentOperatedOffset, 1, SEQUENCE_PARAMETER_SET_TEMP_BUF_LENGTH) == 0 && leadingZeroBits < 128) leadingZeroBits++; val = ((int)(1L << leadingZeroBits) & 0xFFFFFFFF) - 1 + nextBits(rbspBuf, currentOperatedOffset, leadingZeroBits, SEQUENCE_PARAMETER_SET_TEMP_BUF_LENGTH); // retrieve frame_mbs_only_flag m_bFrameMbsOnlyFlag = nextBits(rbspBuf, currentOperatedOffset, 1, SEQUENCE_PARAMETER_SET_TEMP_BUF_LENGTH) != 0; if (m_bFrameMbsOnlyFlag) { val = (2 - 1) * (val + 1); // FrameHeightInMbs = // (2-frame_mbs_only_flag)*(pic_height_in_map_units_minus1+1) m_frameHeightInMbs = (short) val; val = val / (1 + 0); // PicHeightInMbs = // FrameHeightInMbs/(1+field_pic_flag) m_picHeightSampleLuma = (short) (val << 4); // PicHeightInSampleLuma // = PicHeightInMbs*16 if (m_picHeightSampleLuma == 128) m_picHeightSampleLuma = 120; m_dimensionPixel = (m_picWidthSampleLuma << 16) | m_picHeightSampleLuma; } else { val = (2 - 0) * (val + 1); // FrameHeightInMbs = // (2-frame_mbs_only_flag)*(pic_height_in_map_units_minus1+1) m_frameHeightInMbs = (short) val; // val = val/(1+m_bFieldPicFlag ? 1: 0); // PicHeightInMbs = // FrameHeightInMbs/(1+field_pic_flag) // m_picHeightSampleLuma = val<<4; // PicHeightInSampleLuma = // PicHeightInMbs*16 if (m_picHeightSampleLuma == 128) m_picHeightSampleLuma = 120; m_dimensionPixel = (m_picWidthSampleLuma << 16) | m_picHeightSampleLuma; } return true; }
Function nextBits():
/** * Description: read in next bitNum bits and return its value. After * reading, the bit offset value is advanced bitNum bits. * * @param data * pointer to the data to be read * @param bitOffset * the data offset from which the next bitNum bits inclusive will * be read * @param bitNum * the number of bits to be read. The maximum number is 32. * @param limitInBytes * maximum reachable offset in byte * @return */ private int nextBits(byte data[], int bitOffset, int bitNum, int limitInBytes) { int limitBit = limitInBytes << 3; int result = 0; if (bitNum == 0 || bitNum < 0) return result; if (bitNum > 32) return result; if (bitOffset > limitBit) return result; if ((bitOffset + bitNum) > limitBit) return result; int skipByteNum = bitOffset >>> 3; int skipBits = bitOffset % 8; long src = (data[skipByteNum] << skipBits); int leftBits = 8 - skipBits; for (int i = 0; i < bitNum; i++) { result = result << 1; if ((src & 0x80) != 0) result |= 0x01; src = (src << 1); leftBits--; if (leftBits == 0) { src = data[++skipByteNum]; leftBits = 8; } } bitOffset += bitNum; currentOperatedOffset += bitNum; return result; }
Function SkipScalingList():
private void SkipScalingList(int size, byte data[], int bitOffset) { int lastScale = 8; int nextScale = 8; int deltaScale = 0; for (int i = 0; i < size; i++) { if (nextScale != 0) { int leadingZeroBits = 0; // skip first_mb_in_slice data while (nextBits(data, currentOperatedOffset, 1, SEQUENCE_PARAMETER_SET_TEMP_BUF_LENGTH) == 0 && leadingZeroBits < 32) leadingZeroBits++; deltaScale = (1 << leadingZeroBits) - 1 + nextBits(data, currentOperatedOffset, leadingZeroBits, SEQUENCE_PARAMETER_SET_TEMP_BUF_LENGTH); int multi = (deltaScale & 0x1) != 0 ? 1 : -1; int left = deltaScale % 2; if (left == 0) deltaScale = multi * (deltaScale >> 2); else { deltaScale = deltaScale / 2 + 1; deltaScale = multi * deltaScale; } nextScale = (lastScale + deltaScale + 256) % 256; } int scalingList = nextScale != 0 ? lastScale : nextScale; lastScale = scalingList; } }
By using all these code, you can parse the sps information rightly.