text: // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  // Use of this source code is governed by a BSD-style license that can be
  // found in the LICENSE file.
  
  #include "content/common/gpu/media/h264_parser.h"
  
  #include "base/logging.h"
  #include "base/memory/scoped_ptr.h"
  #include "base/stl_util.h"
  
  namespace content {
  
  bool H264SliceHeader::IsPSlice() const {
    return (slice_type % 5 == kPSlice);
  }
  
  bool H264SliceHeader::IsBSlice() const {
    return (slice_type % 5 == kBSlice);
  }
  
  bool H264SliceHeader::IsISlice() const {
    return (slice_type % 5 == kISlice);
  }
  
  bool H264SliceHeader::IsSPSlice() const {
    return (slice_type % 5 == kSPSlice);
  }
  
  bool H264SliceHeader::IsSISlice() const {
    return (slice_type % 5 == kSISlice);
  }
  
  H264NALU::H264NALU() {
    memset(this, 0, sizeof(*this));
  }
  
  H264SPS::H264SPS() {
    memset(this, 0, sizeof(*this));
  }
  
  H264PPS::H264PPS() {
    memset(this, 0, sizeof(*this));
  }
  
  H264SliceHeader::H264SliceHeader() {
    memset(this, 0, sizeof(*this));
  }
  
  H264SEIMessage::H264SEIMessage() {
    memset(this, 0, sizeof(*this));
  }
  
  #define READ_BITS_OR_RETURN(num_bits, out)                                    \
  do {                                                                          \
    int _out;                                                                   \
    if (!br_.ReadBits(num_bits, &_out)) {                                       \
      DVLOG(1) << "Error in stream: unexpected EOS while trying to read " #out; \
      return kInvalidStream;                                                    \
    }                                                                           \
    *out = _out;                                                                \
  } while (0)
  
  #define READ_BOOL_OR_RETURN(out)                                              \
  do {                                                                          \
    int _out;                                                                   \
    if (!br_.ReadBits(1, &_out)) {                                              \
      DVLOG(1) << "Error in stream: unexpected EOS while trying to read " #out; \
      return kInvalidStream;                                                    \
    }                                                                           \
    *out = _out != 0;                                                           \
  } while (0)
  
  #define READ_UE_OR_RETURN(out)                                               \
  do {                                                                         \
    if (ReadUE(out) != kOk) {                                                  \
      DVLOG(1) << "Error in stream: invalid value while trying to read " #out; \
      return kInvalidStream;                                                   \
    }                                                                          \
  } while (0)
  
  #define READ_SE_OR_RETURN(out)                                               \
  do {                                                                         \
    if (ReadSE(out) != kOk) {                                                  \
      DVLOG(1) << "Error in stream: invalid value while trying to read " #out; \
      return kInvalidStream;                                                   \
    }                                                                          \
  } while (0)
  
  #define IN_RANGE_OR_RETURN(val, min, max)                                 \
  do {                                                                      \
    if ((val) < (min) || (val) > (max)) {                                   \
      DVLOG(1) << "Error in stream: invalid value, expected " #val " to be" \
               << " in range [" << (min) << ":" << (max) << "]"             \
               << " found " << (val) << " instead";                         \
      return kInvalidStream;                                                \
    }                                                                       \
  } while (0)
  
  #define TRUE_OR_RETURN(a)                                          \
  do {                                                               \
    if (!(a)) {                                                      \
      DVLOG(1) << "Error in stream: invalid value, expected " << #a; \
      return kInvalidStream;                                         \
    }                                                                \
  } while (0)
  
  H264Parser::H264Parser() {
    Reset();
  }
  
  H264Parser::~H264Parser() {
    STLDeleteValues(&active_SPSes_);
    STLDeleteValues(&active_PPSes_);
  }
  
  void H264Parser::Reset() {
    stream_ = NULL;
    bytes_left_ = 0;
  }
  
  void H264Parser::SetStream(const uint8* stream, off_t stream_size) {
    DCHECK(stream);
    DCHECK(stream_size > 0);
  
    stream_ = stream;
    bytes_left_ = stream_size;
  }
  
  const H264PPS* H264Parser::GetPPS(int pps_id) {
    return active_PPSes_[pps_id];
  }
  
  const H264SPS* H264Parser::GetSPS(int sps_id) {
    return active_SPSes_[sps_id];
  }
  
  static inline bool IsStartCode(const uint8* data) {
    return data[0] == 0x00 && data[1] == 0x00 && data[2] == 0x01;
  }
  
  // Find offset from start of data to next NALU start code
  // and size of found start code (3 or 4 bytes).
  static bool FindStartCode(const uint8* data, off_t data_size,
                            off_t* offset,
                            off_t* start_code_size) {
    off_t bytes_left = data_size;
  
    while (bytes_left > 3) {
      if (IsStartCode(data)) {
        // Found three-byte start code, set pointer at its beginning.
        *offset = data_size - bytes_left;
        *start_code_size = 3;
  
        // If there is a zero byte before this start code,
        // then it's actually a four-byte start code, so backtrack one byte.
        if (*offset > 0 && *(data - 1) == 0x00) {
          --(*offset);
          ++(*start_code_size);
        }
  
        return true;
      }
  
      ++data;
      --bytes_left;
    }
  
    // End of data.
    return false;
  }
  
  // Find the next NALU in stream, returning its start offset without the start
  // code (i.e. at the beginning of NALU data).
  // Size will include trailing zero bits, and will be from start offset to
  // before the start code of the next NALU (or end of stream).
  static bool LocateNALU(const uint8* stream, off_t stream_size,
                         off_t* nalu_start_off, off_t* nalu_size) {
    off_t start_code_size;
  
    // Find start code of the next NALU.
    if (!FindStartCode(stream, stream_size, nalu_start_off, &start_code_size)) {
      DVLOG(4) << "Could not find start code, end of stream?";
      return false;
    }
  
    // Discard its start code.
    *nalu_start_off += start_code_size;
    // Move the stream to the beginning of it (skip the start code).
    stream_size -= *nalu_start_off;
    stream += *nalu_start_off;
  
    // Find the start code of next NALU; if successful, NALU size is the number
    // of bytes from after previous start code to before this one;
    // if next start code is not found, it is still a valid NALU if there
    // are still some bytes left after the first start code.
    // nalu_size is the offset to the next start code
    if (!FindStartCode(stream, stream_size, nalu_size, &start_code_size)) {
      // end of stream (no next NALU), but still valid NALU if any bytes left
      *nalu_size = stream_size;
      if (*nalu_size < 1) {
        DVLOG(3) << "End of stream";
        return false;
      }
    }
  
    return true;
  }
  
  H264Parser::Result H264Parser::ReadUE(int* val) {
    int num_bits = -1;
    int bit;
    int rest;
  
    // Count the number of contiguous zero bits.
    do {
      READ_BITS_OR_RETURN(1, &bit);
      num_bits++;
    } while (bit == 0);
  
    if (num_bits > 31)
      return kInvalidStream;
  
    // Calculate exp-Golomb code value of size num_bits.
    *val = (1 << num_bits) - 1;
  
    if (num_bits > 0) {
      READ_BITS_OR_RETURN(num_bits, &rest);
      *val += rest;
    }
  
    return kOk;
  }
  
  H264Parser::Result H264Parser::ReadSE(int* val) {
    int ue;
    Result res;
  
    // See Chapter 9 in the spec.
    res = ReadUE(&ue);
    if (res != kOk)
      return res;
  
    if (ue % 2 == 0)
      *val = -(ue / 2);
    else
      *val = ue / 2 + 1;
  
    return kOk;
  }
  
  H264Parser::Result H264Parser::AdvanceToNextNALU(H264NALU *nalu) {
    int data;
    off_t off_to_nalu_start;
  
    if (!LocateNALU(stream_, bytes_left_, &off_to_nalu_start, &nalu->size)) {
      DVLOG(4) << "Could not find next NALU, bytes left in stream: "
               << bytes_left_;
      return kEOStream;
    }
  
    nalu->data = stream_ + off_to_nalu_start;
  
    // Initialize bit reader at the start of found NALU.
    if (!br_.Initialize(nalu->data, nalu->size))
      return kEOStream;
  
    DVLOG(4) << "Looking for NALU, Stream bytes left: " << bytes_left_
             << " off to next nalu: " << off_to_nalu_start;
  
    // Move parser state to after this NALU, so next time AdvanceToNextNALU
    // is called, we will effectively be skipping it;
    // other parsing functions will use the position saved
    // in bit reader for parsing, so we don't have to remember it here.
    stream_ += off_to_nalu_start + nalu->size;
    bytes_left_ -= off_to_nalu_start + nalu->size;
  
    // Read NALU header, skip the forbidden_zero_bit, but check for it.
    READ_BITS_OR_RETURN(1, &data);
    TRUE_OR_RETURN(data == 0);
  
    READ_BITS_OR_RETURN(2, &nalu->nal_ref_idc);
    READ_BITS_OR_RETURN(5, &nalu->nal_unit_type);
  
    DVLOG(4) << "NALU type: " << (int)nalu->nal_unit_type
             << " at: " << (void*)nalu->data << " size: " << nalu->size
             << " ref: " << (int)nalu->nal_ref_idc;
  
    return kOk;
  }
  
  // Default scaling lists (per spec).
  static const int kDefault4x4Intra[kH264ScalingList4x4Length] = {
    6, 13, 13, 20, 20, 20, 28, 28, 28, 28, 32, 32, 32, 37, 37, 42, };
  
  static const int kDefault4x4Inter[kH264ScalingList4x4Length] = {
    10, 14, 14, 20, 20, 20, 24, 24, 24, 24, 27, 27, 27, 30, 30, 34, };
  
  static const int kDefault8x8Intra[kH264ScalingList8x8Length] = {
    6, 10, 10, 13, 11, 13, 16, 16, 16, 16, 18, 18, 18, 18, 18, 23,
    23, 23, 23, 23, 23, 25, 25, 25, 25, 25, 25, 25, 27, 27, 27, 27,
    27, 27, 27, 27, 29, 29, 29, 29, 29, 29, 29, 31, 31, 31, 31, 31,
    31, 33, 33, 33, 33, 33, 36, 36, 36, 36, 38, 38, 38, 40, 40, 42, };
  
  static const int kDefault8x8Inter[kH264ScalingList8x8Length] = {
    9, 13, 13, 15, 13, 15, 17, 17, 17, 17, 19, 19, 19, 19, 19, 21,
    21, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22, 22, 24, 24, 24, 24,
    24, 24, 24, 24, 25, 25, 25, 25, 25, 25, 25, 27, 27, 27, 27, 27,
    27, 28, 28, 28, 28, 28, 30, 30, 30, 30, 32, 32, 32, 33, 33, 35, };
  
  static inline void DefaultScalingList4x4(
      int i,
      int scaling_list4x4[][kH264ScalingList4x4Length]) {
    DCHECK(i < 6);
  
    if (i < 3)
      memcpy(scaling_list4x4[i], kDefault4x4Intra, sizeof(kDefault4x4Intra));
    else if (i < 6)
      memcpy(scaling_list4x4[i], kDefault4x4Inter, sizeof(kDefault4x4Inter));
  }
  
  static inline void DefaultScalingList8x8(
      int i,
      int scaling_list8x8[][kH264ScalingList8x8Length]) {
    DCHECK(i < 6);
  
    if (i % 2 == 0)
      memcpy(scaling_list8x8[i], kDefault8x8Intra, sizeof(kDefault8x8Intra));
    else
      memcpy(scaling_list8x8[i], kDefault8x8Inter, sizeof(kDefault8x8Inter));
  }
  
  static void FallbackScalingList4x4(
      int i,
      const int default_scaling_list_intra[],
      const int default_scaling_list_inter[],
      int scaling_list4x4[][kH264ScalingList4x4Length]) {
    static const int kScalingList4x4ByteSize = sizeof(scaling_list4x4[0][0]) *
        kH264ScalingList4x4Length;
  
    switch (i) {
      case 0:
        memcpy(scaling_list4x4[i], default_scaling_list_intra,
               kScalingList4x4ByteSize);
        break;
  
      case 1:
        memcpy(scaling_list4x4[i], scaling_list4x4[0],
               kScalingList4x4ByteSize);
        break;
  
      case 2:
        memcpy(scaling_list4x4[i], scaling_list4x4[1],
               kScalingList4x4ByteSize);
        break;
  
      case 3:
        memcpy(scaling_list4x4[i], default_scaling_list_inter,
               kScalingList4x4ByteSize);
        break;
  
      case 4:
        memcpy(scaling_list4x4[i], scaling_list4x4[3],
               kScalingList4x4ByteSize);
        break;
  
      case 5:
        memcpy(scaling_list4x4[i], scaling_list4x4[4],
               kScalingList4x4ByteSize);
        break;
  
      default:
        NOTREACHED();
        break;
    }
  }
  
  static void FallbackScalingList8x8(
      int i,
      const int default_scaling_list_intra[],
      const int default_scaling_list_inter[],
      int scaling_list8x8[][kH264ScalingList8x8Length]) {
    static const int kScalingList8x8ByteSize = sizeof(scaling_list8x8[0][0]) *
        kH264ScalingList8x8Length;
  
    switch (i) {
      case 0:
        memcpy(scaling_list8x8[i], default_scaling_list_intra,
               kScalingList8x8ByteSize);
        break;
  
      case 1:
        memcpy(scaling_list8x8[i], default_scaling_list_inter,
               kScalingList8x8ByteSize);
        break;
  
      case 2:
        memcpy(scaling_list8x8[i], scaling_list8x8[0],
               kScalingList8x8ByteSize);
        break;
  
      case 3:
        memcpy(scaling_list8x8[i], scaling_list8x8[1],
               kScalingList8x8ByteSize);
        break;
  
      case 4:
        memcpy(scaling_list8x8[i], scaling_list8x8[2],
               kScalingList8x8ByteSize);
        break;
  
      case 5:
        memcpy(scaling_list8x8[i], scaling_list8x8[3],
               kScalingList8x8ByteSize);
        break;
  
      default:
        NOTREACHED();
        break;
    }
  }
  
  H264Parser::Result H264Parser::ParseScalingList(int size,
                                                  int* scaling_list,
                                                  bool* use_default) {
    // See chapter 7.3.2.1.1.1.
    int last_scale = 8;
    int next_scale = 8;
    int delta_scale;
  
    *use_default = false;
  
    for (int j = 0; j < size; ++j) {
      if (next_scale != 0) {
        READ_SE_OR_RETURN(&delta_scale);
        IN_RANGE_OR_RETURN(delta_scale, -128, 127);
        next_scale = (last_scale + delta_scale + 256) & 0xff;
  
        if (j == 0 && next_scale == 0) {
          *use_default = true;
          return kOk;
        }
      }
  
      scaling_list[j] = (next_scale == 0) ? last_scale : next_scale;
      last_scale = scaling_list[j];
    }
  
    return kOk;
  }
  
  H264Parser::Result H264Parser::ParseSPSScalingLists(H264SPS* sps) {
    // See 7.4.2.1.1.
    bool seq_scaling_list_present_flag;
    bool use_default;
    Result res;
  
    // Parse scaling_list4x4.
    for (int i = 0; i < 6; ++i) {
      READ_BOOL_OR_RETURN(&seq_scaling_list_present_flag);
  
      if (seq_scaling_list_present_flag) {
        res = ParseScalingList(sizeof(sps->scaling_list4x4[i]),
                               sps->scaling_list4x4[i], &use_default);
        if (res != kOk)
          return res;
  
        if (use_default)
          DefaultScalingList4x4(i, sps->scaling_list4x4);
  
      } else {
          FallbackScalingList4x4(i, kDefault4x4Intra, kDefault4x4Inter,
                                 sps->scaling_list4x4);
      }
    }
  
    // Parse scaling_list8x8.
    for (int i = 0; i < ((sps->chroma_format_idc != 3) ? 2 : 6); ++i) {
      READ_BOOL_OR_RETURN(&seq_scaling_list_present_flag);
  
      if (seq_scaling_list_present_flag) {
        res = ParseScalingList(sizeof(sps->scaling_list8x8[i]),
                               sps->scaling_list8x8[i], &use_default);
        if (res != kOk)
          return res;
  
        if (use_default)
          DefaultScalingList8x8(i, sps->scaling_list8x8);
  
      } else {
          FallbackScalingList8x8(i, kDefault8x8Intra, kDefault8x8Inter,
                                sps->scaling_list8x8);
      }
    }
  
    return kOk;
  }
  
  H264Parser::Result H264Parser::ParsePPSScalingLists(const H264SPS& sps,
                                                      H264PPS* pps) {
    // See 7.4.2.2.
    bool pic_scaling_list_present_flag;
    bool use_default;
    Result res;
  
    for (int i = 0; i < 6; ++i) {
      READ_BOOL_OR_RETURN(&pic_scaling_list_present_flag);
  
      if (pic_scaling_list_present_flag) {
        res = ParseScalingList(sizeof(pps->scaling_list4x4[i]),
                               pps->scaling_list4x4[i], &use_default);
        if (res != kOk)
          return res;
  
        if (use_default)
          DefaultScalingList4x4(i, pps->scaling_list4x4);
  
      } else {
        if (sps.seq_scaling_matrix_present_flag) {
          // Table 7-2 fallback rule A in spec.
          FallbackScalingList4x4(i, kDefault4x4Intra, kDefault4x4Inter,
                                 pps->scaling_list4x4);
        } else {
          // Table 7-2 fallback rule B in spec.
          FallbackScalingList4x4(i, sps.scaling_list4x4[0],
                                 sps.scaling_list4x4[3], pps->scaling_list4x4);
        }
      }
    }
  
    if (pps->transform_8x8_mode_flag) {
      for (int i = 0; i < ((sps.chroma_format_idc != 3) ? 2 : 6); ++i) {
        READ_BOOL_OR_RETURN(&pic_scaling_list_present_flag);
  
        if (pic_scaling_list_present_flag) {
          res = ParseScalingList(sizeof(pps->scaling_list8x8[i]),
                                 pps->scaling_list8x8[i], &use_default);
          if (res != kOk)
            return res;
  
          if (use_default)
            DefaultScalingList8x8(i, pps->scaling_list8x8);
  
        } else {
          if (sps.seq_scaling_matrix_present_flag) {
            // Table 7-2 fallback rule A in spec.
            FallbackScalingList8x8(i, kDefault8x8Intra,
                                   kDefault8x8Inter, pps->scaling_list8x8);
          } else {
            // Table 7-2 fallback rule B in spec.
            FallbackScalingList8x8(i, sps.scaling_list8x8[0],
                                   sps.scaling_list8x8[1], pps->scaling_list8x8);
          }
        }
      }
    }
    return kOk;
  }
  
  static void FillDefaultSeqScalingLists(H264SPS* sps) {
    for (int i = 0; i < 6; ++i)
      for (int j = 0; j < kH264ScalingList4x4Length; ++j)
        sps->scaling_list4x4[i][j] = 16;
  
    for (int i = 0; i < 6; ++i)
      for (int j = 0; j < kH264ScalingList8x8Length; ++j)
        sps->scaling_list8x8[i][j] = 16;
  
  }
  
  H264Parser::Result H264Parser::ParseSPS(int* sps_id) {
    // See 7.4.2.1.
    int data;
    Result res;
  
    *sps_id = -1;
  
    scoped_ptr<H264SPS> sps(new H264SPS());
  
    READ_BITS_OR_RETURN(8, &sps->profile_idc);
    READ_BITS_OR_RETURN(6, &sps->constraint_setx_flag);
    READ_BITS_OR_RETURN(2, &data);
    READ_BITS_OR_RETURN(8, &sps->level_idc);
    READ_UE_OR_RETURN(&sps->seq_parameter_set_id);
    TRUE_OR_RETURN(sps->seq_parameter_set_id < 32);
  
    if (sps->profile_idc == 100 || sps->profile_idc == 110 ||
        sps->profile_idc == 122 || sps->profile_idc == 244 ||
        sps->profile_idc ==  44 || sps->profile_idc ==  83 ||
        sps->profile_idc ==  86 || sps->profile_idc == 118 ||
        sps->profile_idc == 128) {
      READ_UE_OR_RETURN(&sps->chroma_format_idc);
      TRUE_OR_RETURN(sps->chroma_format_idc < 4);
  
      if (sps->chroma_format_idc == 3)
        READ_BOOL_OR_RETURN(&sps->separate_colour_plane_flag);
  
      READ_UE_OR_RETURN(&sps->bit_depth_luma_minus8);
      TRUE_OR_RETURN(sps->bit_depth_luma_minus8 < 7);
  
      READ_UE_OR_RETURN(&sps->bit_depth_chroma_minus8);
      TRUE_OR_RETURN(sps->bit_depth_chroma_minus8 < 7);
  
      READ_BOOL_OR_RETURN(&sps->qpprime_y_zero_transform_bypass_flag);
      READ_BOOL_OR_RETURN(&sps->seq_scaling_matrix_present_flag);
  
      if (sps->seq_scaling_matrix_present_flag) {
        DVLOG(4) << "Scaling matrix present";
        res = ParseSPSScalingLists(sps.get());
        if (res != kOk)
          return res;
      } else {
        FillDefaultSeqScalingLists(sps.get());
      }
    } else {
      sps->chroma_format_idc = 1;
      FillDefaultSeqScalingLists(sps.get());
    }
  
    if (sps->separate_colour_plane_flag)
      sps->chroma_array_type = 0;
    else
      sps->chroma_array_type = sps->chroma_format_idc;
  
    READ_UE_OR_RETURN(&sps->log2_max_frame_num_minus4);
    TRUE_OR_RETURN(sps->log2_max_frame_num_minus4 < 13);
  
    READ_UE_OR_RETURN(&sps->pic_order_cnt_type);
    TRUE_OR_RETURN(sps->pic_order_cnt_type < 3);
  
    sps->expected_delta_per_pic_order_cnt_cycle = 0;
    if (sps->pic_order_cnt_type == 0) {
      READ_UE_OR_RETURN(&sps->log2_max_pic_order_cnt_lsb_minus4);
      TRUE_OR_RETURN(sps->log2_max_pic_order_cnt_lsb_minus4 < 13);
    } else if (sps->pic_order_cnt_type == 1) {
      READ_BOOL_OR_RETURN(&sps->delta_pic_order_always_zero_flag);
      READ_SE_OR_RETURN(&sps->offset_for_non_ref_pic);
      READ_SE_OR_RETURN(&sps->offset_for_top_to_bottom_field);
      READ_UE_OR_RETURN(&sps->num_ref_frames_in_pic_order_cnt_cycle);
      TRUE_OR_RETURN(sps->num_ref_frames_in_pic_order_cnt_cycle < 255);
  
      for (int i = 0; i < sps->num_ref_frames_in_pic_order_cnt_cycle; ++i) {
        READ_SE_OR_RETURN(&sps->offset_for_ref_frame[i]);
        sps->expected_delta_per_pic_order_cnt_cycle +=
            sps->offset_for_ref_frame[i];
      }
    }
  
    READ_UE_OR_RETURN(&sps->max_num_ref_frames);
    READ_BOOL_OR_RETURN(&sps->gaps_in_frame_num_value_allowed_flag);
  
    if (sps->gaps_in_frame_num_value_allowed_flag)
      return kUnsupportedStream;
  
    READ_UE_OR_RETURN(&sps->pic_width_in_mbs_minus1);
    READ_UE_OR_RETURN(&sps->pic_height_in_map_units_minus1);
  
    READ_BOOL_OR_RETURN(&sps->frame_mbs_only_flag);
    if (!sps->frame_mbs_only_flag)
      READ_BOOL_OR_RETURN(&sps->mb_adaptive_frame_field_flag);
  
    READ_BOOL_OR_RETURN(&sps->direct_8x8_inference_flag);
  
    READ_BOOL_OR_RETURN(&sps->frame_cropping_flag);
    if (sps->frame_cropping_flag) {
      READ_UE_OR_RETURN(&sps->frame_crop_left_offset);
      READ_UE_OR_RETURN(&sps->frame_crop_right_offset);
      READ_UE_OR_RETURN(&sps->frame_crop_top_offset);
      READ_UE_OR_RETURN(&sps->frame_crop_bottom_offset);
    }
  
    READ_BOOL_OR_RETURN(&sps->vui_parameters_present_flag);
    if (sps->vui_parameters_present_flag) {
      DVLOG(1) << "VUI parameters present in SPS, ignoring";
    }
  
    // If an SPS with the same id already exists, replace it.
    *sps_id = sps->seq_parameter_set_id;
    delete active_SPSes_[*sps_id];
    active_SPSes_[*sps_id] = sps.release();
  
    return kOk;
  }
  
  H264Parser::Result H264Parser::ParsePPS(int* pps_id) {
    // See 7.4.2.2.
    const H264SPS* sps;
    Result res;
  
    *pps_id = -1;
  
    scoped_ptr<H264PPS> pps(new H264PPS());
  
    READ_UE_OR_RETURN(&pps->pic_parameter_set_id);
    READ_UE_OR_RETURN(&pps->seq_parameter_set_id);
    TRUE_OR_RETURN(pps->seq_parameter_set_id < 32);
  
    sps = GetSPS(pps->seq_parameter_set_id);
    TRUE_OR_RETURN(sps);
  
    READ_BOOL_OR_RETURN(&pps->entropy_coding_mode_flag);
    READ_BOOL_OR_RETURN(&pps->bottom_field_pic_order_in_frame_present_flag);
  
    READ_UE_OR_RETURN(&pps->num_slice_groups_minus1);
    if (pps->num_slice_groups_minus1 > 1) {
      DVLOG(1) << "Slice groups not supported";
      return kUnsupportedStream;
    }
  
    READ_UE_OR_RETURN(&pps->num_ref_idx_l0_default_active_minus1);
    TRUE_OR_RETURN(pps->num_ref_idx_l0_default_active_minus1 < 32);
  
    READ_UE_OR_RETURN(&pps->num_ref_idx_l1_default_active_minus1);
    TRUE_OR_RETURN(pps->num_ref_idx_l1_default_active_minus1 < 32);
  
    READ_BOOL_OR_RETURN(&pps->weighted_pred_flag);
    READ_BITS_OR_RETURN(2, &pps->weighted_bipred_idc);
    TRUE_OR_RETURN(pps->weighted_bipred_idc < 3);
  
    READ_SE_OR_RETURN(&pps->pic_init_qp_minus26);
    IN_RANGE_OR_RETURN(pps->pic_init_qp_minus26, -26, 25);
  
    READ_SE_OR_RETURN(&pps->pic_init_qs_minus26);
    IN_RANGE_OR_RETURN(pps->pic_init_qs_minus26, -26, 25);
  
    READ_SE_OR_RETURN(&pps->chroma_qp_index_offset);
    IN_RANGE_OR_RETURN(pps->chroma_qp_index_offset, -12, 12);
    pps->second_chroma_qp_index_offset = pps->chroma_qp_index_offset;
  
    READ_BOOL_OR_RETURN(&pps->deblocking_filter_control_present_flag);
    READ_BOOL_OR_RETURN(&pps->constrained_intra_pred_flag);
    READ_BOOL_OR_RETURN(&pps->redundant_pic_cnt_present_flag);
  
    if (br_.HasMoreRBSPData()) {
      READ_BOOL_OR_RETURN(&pps->transform_8x8_mode_flag);
      READ_BOOL_OR_RETURN(&pps->pic_scaling_matrix_present_flag);
  
      if (pps->pic_scaling_matrix_present_flag) {
        DVLOG(4) << "Picture scaling matrix present";
        res = ParsePPSScalingLists(*sps, pps.get());
        if (res != kOk)
          return res;
      }
  
      READ_SE_OR_RETURN(&pps->second_chroma_qp_index_offset);
    }
  
    // If a PPS with the same id already exists, replace it.
    *pps_id = pps->pic_parameter_set_id;
    delete active_PPSes_[*pps_id];
    active_PPSes_[*pps_id] = pps.release();
  
    return kOk;
  }
  
  H264Parser::Result H264Parser::ParseRefPicListModification(
      int num_ref_idx_active_minus1,
      H264ModificationOfPicNum* ref_list_mods) {
    H264ModificationOfPicNum* pic_num_mod;
  
    if (num_ref_idx_active_minus1 >= 32)
      return kInvalidStream;
  
    for (int i = 0; i < 32; ++i) {
      pic_num_mod = &ref_list_mods[i];
      READ_UE_OR_RETURN(&pic_num_mod->modification_of_pic_nums_idc);
      TRUE_OR_RETURN(pic_num_mod->modification_of_pic_nums_idc < 4);
  
      switch (pic_num_mod->modification_of_pic_nums_idc) {
        case 0:
        case 1:
          READ_UE_OR_RETURN(&pic_num_mod->abs_diff_pic_num_minus1);
          break;
  
        case 2:
          READ_UE_OR_RETURN(&pic_num_mod->long_term_pic_num);
          break;
  
        case 3:
          // Per spec, list cannot be empty.
          if (i == 0)
            return kInvalidStream;
          return kOk;
  
        default:
          return kInvalidStream;
      }
    }
  
    // If we got here, we didn't get loop end marker prematurely,
    // so make sure it is there for our client.
    int modification_of_pic_nums_idc;
    READ_UE_OR_RETURN(&modification_of_pic_nums_idc);
    TRUE_OR_RETURN(modification_of_pic_nums_idc == 3);
  
    return kOk;
  }
  
  H264Parser::Result H264Parser::ParseRefPicListModifications(
      H264SliceHeader* shdr) {
    Result res;
  
    if (!shdr->IsISlice() && !shdr->IsSISlice()) {
      READ_BOOL_OR_RETURN(&shdr->ref_pic_list_modification_flag_l0);
      if (shdr->ref_pic_list_modification_flag_l0) {
        res = ParseRefPicListModification(shdr->num_ref_idx_l0_active_minus1,
                                          shdr->ref_list_l0_modifications);
        if (res != kOk)
          return res;
      }
    }
  
    if (shdr->IsBSlice()) {
      READ_BOOL_OR_RETURN(&shdr->ref_pic_list_modification_flag_l1);
      if (shdr->ref_pic_list_modification_flag_l1) {
        res = ParseRefPicListModification(shdr->num_ref_idx_l1_active_minus1,
                                          shdr->ref_list_l1_modifications);
        if (res != kOk)
          return res;
      }
    }
  
    return kOk;
  }
  
  H264Parser::Result H264Parser::ParseWeightingFactors(
      int num_ref_idx_active_minus1,
      int chroma_array_type,
      int luma_log2_weight_denom,
      int chroma_log2_weight_denom,
      H264WeightingFactors* w_facts) {
  
    int def_luma_weight = 1 << luma_log2_weight_denom;
    int def_chroma_weight = 1 << chroma_log2_weight_denom;
  
    for (int i = 0; i < num_ref_idx_active_minus1 + 1; ++i) {
      READ_BOOL_OR_RETURN(&w_facts->luma_weight_flag);
      if (w_facts->luma_weight_flag) {
        READ_SE_OR_RETURN(&w_facts->luma_weight[i]);
        IN_RANGE_OR_RETURN(w_facts->luma_weight[i], -128, 127);
  
        READ_SE_OR_RETURN(&w_facts->luma_offset[i]);
        IN_RANGE_OR_RETURN(w_facts->luma_offset[i], -128, 127);
      } else {
        w_facts->luma_weight[i] = def_luma_weight;
        w_facts->luma_offset[i] = 0;
      }
  
      if (chroma_array_type != 0) {
        READ_BOOL_OR_RETURN(&w_facts->chroma_weight_flag);
        if (w_facts->chroma_weight_flag) {
          for (int j = 0; j < 2; ++j) {
            READ_SE_OR_RETURN(&w_facts->chroma_weight[i][j]);
            IN_RANGE_OR_RETURN(w_facts->chroma_weight[i][j], -128, 127);
  
            READ_SE_OR_RETURN(&w_facts->chroma_offset[i][j]);
            IN_RANGE_OR_RETURN(w_facts->chroma_offset[i][j], -128, 127);
          }
        } else {
          for (int j = 0; j < 2; ++j) {
            w_facts->chroma_weight[i][j] = def_chroma_weight;
            w_facts->chroma_offset[i][j] = 0;
          }
        }
      }
    }
  
    return kOk;
  }
  
  H264Parser::Result H264Parser::ParsePredWeightTable(const H264SPS& sps,
                                                      H264SliceHeader* shdr) {
    READ_UE_OR_RETURN(&shdr->luma_log2_weight_denom);
    TRUE_OR_RETURN(shdr->luma_log2_weight_denom < 8);
  
    if (sps.chroma_array_type != 0)
      READ_UE_OR_RETURN(&shdr->chroma_log2_weight_denom);
    TRUE_OR_RETURN(shdr->chroma_log2_weight_denom < 8);
  
    Result res = ParseWeightingFactors(shdr->num_ref_idx_l0_active_minus1,
                                       sps.chroma_array_type,
                                       shdr->luma_log2_weight_denom,
                                       shdr->chroma_log2_weight_denom,
                                       &shdr->pred_weight_table_l0);
    if (res != kOk)
      return res;
  
    if (shdr->IsBSlice()) {
      res = ParseWeightingFactors(shdr->num_ref_idx_l1_active_minus1,
                                  sps.chroma_array_type,
                                  shdr->luma_log2_weight_denom,
                                  shdr->chroma_log2_weight_denom,
                                  &shdr->pred_weight_table_l1);
      if (res != kOk)
        return res;
    }
  
    return kOk;
  }
  
  H264Parser::Result H264Parser::ParseDecRefPicMarking(H264SliceHeader *shdr) {
    if (shdr->idr_pic_flag) {
      READ_BOOL_OR_RETURN(&shdr->no_output_of_prior_pics_flag);
      READ_BOOL_OR_RETURN(&shdr->long_term_reference_flag);
    } else {
      READ_BOOL_OR_RETURN(&shdr->adaptive_ref_pic_marking_mode_flag);
  
      H264DecRefPicMarking* marking;
      if (shdr->adaptive_ref_pic_marking_mode_flag) {
        size_t i;
        for (i = 0; i < arraysize(shdr->ref_pic_marking); ++i) {
          marking = &shdr->ref_pic_marking[i];
  
          READ_UE_OR_RETURN(&marking->memory_mgmnt_control_operation);
          if (marking->memory_mgmnt_control_operation == 0)
            break;
  
          if (marking->memory_mgmnt_control_operation == 1 ||
              marking->memory_mgmnt_control_operation == 3)
            READ_UE_OR_RETURN(&marking->difference_of_pic_nums_minus1);
  
          if (marking->memory_mgmnt_control_operation == 2)
            READ_UE_OR_RETURN(&marking->long_term_pic_num);
  
          if (marking->memory_mgmnt_control_operation == 3 ||
              marking->memory_mgmnt_control_operation == 6)
            READ_UE_OR_RETURN(&marking->long_term_frame_idx);
  
          if (marking->memory_mgmnt_control_operation == 4)
            READ_UE_OR_RETURN(&marking->max_long_term_frame_idx_plus1);
  
          if (marking->memory_mgmnt_control_operation > 6)
            return kInvalidStream;
        }
  
        if (i == arraysize(shdr->ref_pic_marking)) {
          DVLOG(1) << "Ran out of dec ref pic marking fields";
          return kUnsupportedStream;
        }
      }
    }
  
    return kOk;
  }
  
  H264Parser::Result H264Parser::ParseSliceHeader(const H264NALU& nalu,
                                                  H264SliceHeader* shdr) {
    // See 7.4.3.
    const H264SPS* sps;
    const H264PPS* pps;
    Result res;
  
    memset(shdr, 0, sizeof(*shdr));
  
    shdr->idr_pic_flag = ((nalu.nal_unit_type == 5) ? true : false);
    shdr->nal_ref_idc = nalu.nal_ref_idc;
    shdr->nalu_data = nalu.data;
    shdr->nalu_size = nalu.size;
  
    READ_UE_OR_RETURN(&shdr->first_mb_in_slice);
    READ_UE_OR_RETURN(&shdr->slice_type);
    TRUE_OR_RETURN(shdr->slice_type < 10);
  
    READ_UE_OR_RETURN(&shdr->pic_parameter_set_id);
  
    pps = GetPPS(shdr->pic_parameter_set_id);
    TRUE_OR_RETURN(pps);
  
    sps = GetSPS(pps->seq_parameter_set_id);
    TRUE_OR_RETURN(sps);
  
    if (sps->separate_colour_plane_flag) {
      DVLOG(1) << "Interlaced streams not supported";
      return kUnsupportedStream;
    }
  
    READ_BITS_OR_RETURN(sps->log2_max_frame_num_minus4 + 4,
                        &shdr->frame_num);
    if (!sps->frame_mbs_only_flag) {
      READ_BOOL_OR_RETURN(&shdr->field_pic_flag);
      if (shdr->field_pic_flag) {
        DVLOG(1) << "Interlaced streams not supported";
        return kUnsupportedStream;
      }
    }
  
    if (shdr->idr_pic_flag)
      READ_UE_OR_RETURN(&shdr->idr_pic_id);
  
    if (sps->pic_order_cnt_type == 0) {
      READ_BITS_OR_RETURN(sps->log2_max_pic_order_cnt_lsb_minus4 + 4,
                          &shdr->pic_order_cnt_lsb);
      if (pps->bottom_field_pic_order_in_frame_present_flag &&
          !shdr->field_pic_flag)
        READ_SE_OR_RETURN(&shdr->delta_pic_order_cnt_bottom);
    }
  
    if (sps->pic_order_cnt_type == 1 && !sps->delta_pic_order_always_zero_flag) {
      READ_SE_OR_RETURN(&shdr->delta_pic_order_cnt[0]);
      if (pps->bottom_field_pic_order_in_frame_present_flag &&
          !shdr->field_pic_flag)
        READ_SE_OR_RETURN(&shdr->delta_pic_order_cnt[1]);
    }
  
    if (pps->redundant_pic_cnt_present_flag) {
      READ_UE_OR_RETURN(&shdr->redundant_pic_cnt);
      TRUE_OR_RETURN(shdr->redundant_pic_cnt < 128);
    }
  
    if (shdr->IsBSlice())
      READ_BOOL_OR_RETURN(&shdr->direct_spatial_mv_pred_flag);
  
    if (shdr->IsPSlice() || shdr->IsSPSlice() || shdr->IsBSlice()) {
      READ_BOOL_OR_RETURN(&shdr->num_ref_idx_active_override_flag);
      if (shdr->num_ref_idx_active_override_flag) {
        READ_UE_OR_RETURN(&shdr->num_ref_idx_l0_active_minus1);
        if (shdr->IsBSlice())
          READ_UE_OR_RETURN(&shdr->num_ref_idx_l1_active_minus1);
      } else {
        shdr->num_ref_idx_l0_active_minus1 =
            pps->num_ref_idx_l0_default_active_minus1;
        if (shdr->IsBSlice()) {
          shdr->num_ref_idx_l1_active_minus1 =
              pps->num_ref_idx_l1_default_active_minus1;
        }
      }
    }
    if (shdr->field_pic_flag) {
      TRUE_OR_RETURN(shdr->num_ref_idx_l0_active_minus1 < 32);
      TRUE_OR_RETURN(shdr->num_ref_idx_l1_active_minus1 < 32);
    } else {
      TRUE_OR_RETURN(shdr->num_ref_idx_l0_active_minus1 < 16);
      TRUE_OR_RETURN(shdr->num_ref_idx_l1_active_minus1 < 16);
    }
  
    if (nalu.nal_unit_type == H264NALU::kCodedSliceExtension) {
      return kUnsupportedStream;
    } else {
      res = ParseRefPicListModifications(shdr);
      if (res != kOk)
        return res;
    }
  
    if ((pps->weighted_pred_flag && (shdr->IsPSlice() || shdr->IsSPSlice())) ||
        (pps->weighted_bipred_idc == 1 && shdr->IsBSlice())) {
      res = ParsePredWeightTable(*sps, shdr);
      if (res != kOk)
        return res;
    }
  
    if (nalu.nal_ref_idc != 0) {
      res = ParseDecRefPicMarking(shdr);
      if (res != kOk)
        return res;
    }
  
    if (pps->entropy_coding_mode_flag &&
        !shdr->IsISlice() && !shdr->IsSISlice()) {
      READ_UE_OR_RETURN(&shdr->cabac_init_idc);
      TRUE_OR_RETURN(shdr->cabac_init_idc < 3);
    }
  
    READ_SE_OR_RETURN(&shdr->slice_qp_delta);
  
    if (shdr->IsSPSlice() || shdr->IsSISlice()) {
      if (shdr->IsSPSlice())
        READ_BOOL_OR_RETURN(&shdr->sp_for_switch_flag);
      READ_SE_OR_RETURN(&shdr->slice_qs_delta);
    }
  
    if (pps->deblocking_filter_control_present_flag) {
      READ_UE_OR_RETURN(&shdr->disable_deblocking_filter_idc);
      TRUE_OR_RETURN(shdr->disable_deblocking_filter_idc < 3);
  
      if (shdr->disable_deblocking_filter_idc != 1) {
        READ_SE_OR_RETURN(&shdr->slice_alpha_c0_offset_div2);
        IN_RANGE_OR_RETURN(shdr->slice_alpha_c0_offset_div2, -6, 6);
  
        READ_SE_OR_RETURN(&shdr->slice_beta_offset_div2);
        IN_RANGE_OR_RETURN(shdr->slice_beta_offset_div2, -6, 6);
      }
    }
  
    if (pps->num_slice_groups_minus1 > 0) {
      DVLOG(1) << "Slice groups not supported";
      return kUnsupportedStream;
    }
  
    shdr->header_bit_size = shdr->nalu_size * 8 - br_.NumBitsLeft();
  
    return kOk;
  }
  
  H264Parser::Result H264Parser::ParseSEI(H264SEIMessage* sei_msg) {
    int byte;
  
    memset(sei_msg, 0, sizeof(*sei_msg));
  
    READ_BITS_OR_RETURN(8, &byte);
    while (byte == 0xff) {
      sei_msg->type += 255;
      READ_BITS_OR_RETURN(8, &byte);
    }
    sei_msg->type += byte;
  
    READ_BITS_OR_RETURN(8, &byte);
    while (byte == 0xff) {
      sei_msg->payload_size += 255;
      READ_BITS_OR_RETURN(8, &byte);
    }
    sei_msg->payload_size += byte;
  
    DVLOG(4) << "Found SEI message type: " << sei_msg->type
             << " payload size: " << sei_msg->payload_size;
  
    switch (sei_msg->type) {
      case H264SEIMessage::kSEIRecoveryPoint:
        READ_UE_OR_RETURN(&sei_msg->recovery_point.recovery_frame_cnt);
        READ_BOOL_OR_RETURN(&sei_msg->recovery_point.exact_match_flag);
        READ_BOOL_OR_RETURN(&sei_msg->recovery_point.broken_link_flag);
        READ_BITS_OR_RETURN(2,
            &sei_msg->recovery_point.changing_slice_group_idc);
        break;
  
      default:
        DVLOG(4) << "Unsupported SEI message";
        break;
    }
  
    return kOk;
  }
  
  }  // namespace content
  