text: // Copyright 2019 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 "media/gpu/v4l2/v4l2_video_decoder_delegate_h264_legacy.h"
  
  #include <linux/media/h264-ctrls-legacy.h>
  #include <linux/videodev2.h>
  #include <type_traits>
  
  #include "base/cxx17_backports.h"
  #include "base/logging.h"
  #include "media/gpu/macros.h"
  #include "media/gpu/v4l2/v4l2_decode_surface.h"
  #include "media/gpu/v4l2/v4l2_decode_surface_handler.h"
  #include "media/gpu/v4l2/v4l2_device.h"
  
  namespace media {
  
  // This struct contains the kernel-specific parts of the H264 acceleration,
  // that we don't want to expose in the .h file since they may differ from
  // upstream.
  struct V4L2VideoDecoderDelegateH264LegacyPrivate {
    // TODO(posciak): This should be queried from hardware once supported.
    static constexpr size_t kMaxSlices = 16;
  
    struct v4l2_ctrl_h264_slice_param v4l2_slice_params[kMaxSlices];
    struct v4l2_ctrl_h264_decode_param v4l2_decode_param;
  };
  
  class V4L2H264Picture : public H264Picture {
   public:
    explicit V4L2H264Picture(scoped_refptr<V4L2DecodeSurface> dec_surface)
        : dec_surface_(std::move(dec_surface)) {}
  
    V4L2H264Picture(const V4L2H264Picture&) = delete;
    V4L2H264Picture& operator=(const V4L2H264Picture&) = delete;
  
    V4L2H264Picture* AsV4L2H264Picture() override { return this; }
    scoped_refptr<V4L2DecodeSurface> dec_surface() { return dec_surface_; }
  
   private:
    ~V4L2H264Picture() override {}
  
    scoped_refptr<V4L2DecodeSurface> dec_surface_;
  };
  
  V4L2VideoDecoderDelegateH264Legacy::V4L2VideoDecoderDelegateH264Legacy(
      V4L2DecodeSurfaceHandler* surface_handler,
      V4L2Device* device)
      : num_slices_(0),
        surface_handler_(surface_handler),
        device_(device),
        priv_(std::make_unique<V4L2VideoDecoderDelegateH264LegacyPrivate>()) {
    DCHECK(surface_handler_);
  }
  
  V4L2VideoDecoderDelegateH264Legacy::~V4L2VideoDecoderDelegateH264Legacy() {}
  
  scoped_refptr<H264Picture>
  V4L2VideoDecoderDelegateH264Legacy::CreateH264Picture() {
    scoped_refptr<V4L2DecodeSurface> dec_surface =
        surface_handler_->CreateSurface();
    if (!dec_surface)
      return nullptr;
  
    return new V4L2H264Picture(dec_surface);
  }
  
  void V4L2VideoDecoderDelegateH264Legacy::H264PictureListToDPBIndicesList(
      const H264Picture::Vector& src_pic_list,
      uint8_t dst_list[kDPBIndicesListSize]) {
    size_t i;
    for (i = 0; i < src_pic_list.size() && i < kDPBIndicesListSize; ++i) {
      const H264Picture* pic = src_pic_list[i].get();
      dst_list[i] = pic ? pic->dpb_position : VIDEO_MAX_FRAME;
    }
  
    while (i < kDPBIndicesListSize)
      dst_list[i++] = VIDEO_MAX_FRAME;
  }
  
  void V4L2VideoDecoderDelegateH264Legacy::H264DPBToV4L2DPB(
      const H264DPB& dpb,
      std::vector<scoped_refptr<V4L2DecodeSurface>>* ref_surfaces) {
    memset(priv_->v4l2_decode_param.dpb, 0, sizeof(priv_->v4l2_decode_param.dpb));
    size_t i = 0;
    for (const auto& pic : dpb) {
      if (i >= base::size(priv_->v4l2_decode_param.dpb)) {
        VLOGF(1) << "Invalid DPB size";
        break;
      }
  
      int index = VIDEO_MAX_FRAME;
      if (!pic->nonexisting) {
        scoped_refptr<V4L2DecodeSurface> dec_surface =
            H264PictureToV4L2DecodeSurface(pic.get());
        index = dec_surface->GetReferenceID();
        ref_surfaces->push_back(dec_surface);
      }
  
      struct v4l2_h264_dpb_entry& entry = priv_->v4l2_decode_param.dpb[i++];
      entry.buf_index = index;
      if (pic->long_term) {
        entry.frame_num = pic->long_term_pic_num;
        entry.pic_num = pic->long_term_frame_idx;
      } else {
        entry.frame_num = pic->frame_num;
        entry.pic_num = pic->pic_num;
      }
      entry.top_field_order_cnt = pic->top_field_order_cnt;
      entry.bottom_field_order_cnt = pic->bottom_field_order_cnt;
      entry.flags = (pic->ref ? V4L2_H264_DPB_ENTRY_FLAG_ACTIVE : 0) |
                    (pic->long_term ? V4L2_H264_DPB_ENTRY_FLAG_LONG_TERM : 0);
    }
  }
  
  H264Decoder::H264Accelerator::Status
  V4L2VideoDecoderDelegateH264Legacy::SubmitFrameMetadata(
      const H264SPS* sps,
      const H264PPS* pps,
      const H264DPB& dpb,
      const H264Picture::Vector& ref_pic_listp0,
      const H264Picture::Vector& ref_pic_listb0,
      const H264Picture::Vector& ref_pic_listb1,
      scoped_refptr<H264Picture> pic) {
    struct v4l2_ext_control ctrl;
    std::vector<struct v4l2_ext_control> ctrls;
  
    struct v4l2_ctrl_h264_sps v4l2_sps;
    memset(&v4l2_sps, 0, sizeof(v4l2_sps));
    v4l2_sps.constraint_set_flags =
        (sps->constraint_set0_flag ? V4L2_H264_SPS_CONSTRAINT_SET0_FLAG : 0) |
        (sps->constraint_set1_flag ? V4L2_H264_SPS_CONSTRAINT_SET1_FLAG : 0) |
        (sps->constraint_set2_flag ? V4L2_H264_SPS_CONSTRAINT_SET2_FLAG : 0) |
        (sps->constraint_set3_flag ? V4L2_H264_SPS_CONSTRAINT_SET3_FLAG : 0) |
        (sps->constraint_set4_flag ? V4L2_H264_SPS_CONSTRAINT_SET4_FLAG : 0) |
        (sps->constraint_set5_flag ? V4L2_H264_SPS_CONSTRAINT_SET5_FLAG : 0);
  #define SPS_TO_V4L2SPS(a) v4l2_sps.a = sps->a
    SPS_TO_V4L2SPS(profile_idc);
    SPS_TO_V4L2SPS(level_idc);
    SPS_TO_V4L2SPS(seq_parameter_set_id);
    SPS_TO_V4L2SPS(chroma_format_idc);
    SPS_TO_V4L2SPS(bit_depth_luma_minus8);
    SPS_TO_V4L2SPS(bit_depth_chroma_minus8);
    SPS_TO_V4L2SPS(log2_max_frame_num_minus4);
    SPS_TO_V4L2SPS(pic_order_cnt_type);
    SPS_TO_V4L2SPS(log2_max_pic_order_cnt_lsb_minus4);
    SPS_TO_V4L2SPS(offset_for_non_ref_pic);
    SPS_TO_V4L2SPS(offset_for_top_to_bottom_field);
    SPS_TO_V4L2SPS(num_ref_frames_in_pic_order_cnt_cycle);
  
    static_assert(std::extent<decltype(v4l2_sps.offset_for_ref_frame)>() ==
                      std::extent<decltype(sps->offset_for_ref_frame)>(),
                  "offset_for_ref_frame arrays must be same size");
    for (size_t i = 0; i < base::size(v4l2_sps.offset_for_ref_frame); ++i)
      v4l2_sps.offset_for_ref_frame[i] = sps->offset_for_ref_frame[i];
    SPS_TO_V4L2SPS(max_num_ref_frames);
    SPS_TO_V4L2SPS(pic_width_in_mbs_minus1);
    SPS_TO_V4L2SPS(pic_height_in_map_units_minus1);
  #undef SPS_TO_V4L2SPS
  
  #define SET_V4L2_SPS_FLAG_IF(cond, flag) \
    v4l2_sps.flags |= ((sps->cond) ? (flag) : 0)
    SET_V4L2_SPS_FLAG_IF(separate_colour_plane_flag,
                         V4L2_H264_SPS_FLAG_SEPARATE_COLOUR_PLANE);
    SET_V4L2_SPS_FLAG_IF(qpprime_y_zero_transform_bypass_flag,
                         V4L2_H264_SPS_FLAG_QPPRIME_Y_ZERO_TRANSFORM_BYPASS);
    SET_V4L2_SPS_FLAG_IF(delta_pic_order_always_zero_flag,
                         V4L2_H264_SPS_FLAG_DELTA_PIC_ORDER_ALWAYS_ZERO);
    SET_V4L2_SPS_FLAG_IF(gaps_in_frame_num_value_allowed_flag,
                         V4L2_H264_SPS_FLAG_GAPS_IN_FRAME_NUM_VALUE_ALLOWED);
    SET_V4L2_SPS_FLAG_IF(frame_mbs_only_flag, V4L2_H264_SPS_FLAG_FRAME_MBS_ONLY);
    SET_V4L2_SPS_FLAG_IF(mb_adaptive_frame_field_flag,
                         V4L2_H264_SPS_FLAG_MB_ADAPTIVE_FRAME_FIELD);
    SET_V4L2_SPS_FLAG_IF(direct_8x8_inference_flag,
                         V4L2_H264_SPS_FLAG_DIRECT_8X8_INFERENCE);
  #undef SET_V4L2_SPS_FLAG_IF
    memset(&ctrl, 0, sizeof(ctrl));
    ctrl.id = V4L2_CID_MPEG_VIDEO_H264_SPS;
    ctrl.size = sizeof(v4l2_sps);
    ctrl.ptr = &v4l2_sps;
    ctrls.push_back(ctrl);
  
    struct v4l2_ctrl_h264_pps v4l2_pps;
    memset(&v4l2_pps, 0, sizeof(v4l2_pps));
  #define PPS_TO_V4L2PPS(a) v4l2_pps.a = pps->a
    PPS_TO_V4L2PPS(pic_parameter_set_id);
    PPS_TO_V4L2PPS(seq_parameter_set_id);
    PPS_TO_V4L2PPS(num_slice_groups_minus1);
    PPS_TO_V4L2PPS(num_ref_idx_l0_default_active_minus1);
    PPS_TO_V4L2PPS(num_ref_idx_l1_default_active_minus1);
    PPS_TO_V4L2PPS(weighted_bipred_idc);
    PPS_TO_V4L2PPS(pic_init_qp_minus26);
    PPS_TO_V4L2PPS(pic_init_qs_minus26);
    PPS_TO_V4L2PPS(chroma_qp_index_offset);
    PPS_TO_V4L2PPS(second_chroma_qp_index_offset);
  #undef PPS_TO_V4L2PPS
  
  #define SET_V4L2_PPS_FLAG_IF(cond, flag) \
    v4l2_pps.flags |= ((pps->cond) ? (flag) : 0)
    SET_V4L2_PPS_FLAG_IF(entropy_coding_mode_flag,
                         V4L2_H264_PPS_FLAG_ENTROPY_CODING_MODE);
    SET_V4L2_PPS_FLAG_IF(
        bottom_field_pic_order_in_frame_present_flag,
        V4L2_H264_PPS_FLAG_BOTTOM_FIELD_PIC_ORDER_IN_FRAME_PRESENT);
    SET_V4L2_PPS_FLAG_IF(weighted_pred_flag, V4L2_H264_PPS_FLAG_WEIGHTED_PRED);
    SET_V4L2_PPS_FLAG_IF(deblocking_filter_control_present_flag,
                         V4L2_H264_PPS_FLAG_DEBLOCKING_FILTER_CONTROL_PRESENT);
    SET_V4L2_PPS_FLAG_IF(constrained_intra_pred_flag,
                         V4L2_H264_PPS_FLAG_CONSTRAINED_INTRA_PRED);
    SET_V4L2_PPS_FLAG_IF(redundant_pic_cnt_present_flag,
                         V4L2_H264_PPS_FLAG_REDUNDANT_PIC_CNT_PRESENT);
    SET_V4L2_PPS_FLAG_IF(transform_8x8_mode_flag,
                         V4L2_H264_PPS_FLAG_TRANSFORM_8X8_MODE);
    SET_V4L2_PPS_FLAG_IF(pic_scaling_matrix_present_flag,
                         V4L2_H264_PPS_FLAG_PIC_SCALING_MATRIX_PRESENT);
  #undef SET_V4L2_PPS_FLAG_IF
    memset(&ctrl, 0, sizeof(ctrl));
    ctrl.id = V4L2_CID_MPEG_VIDEO_H264_PPS;
    ctrl.size = sizeof(v4l2_pps);
    ctrl.ptr = &v4l2_pps;
    ctrls.push_back(ctrl);
  
    struct v4l2_ctrl_h264_scaling_matrix v4l2_scaling_matrix;
    memset(&v4l2_scaling_matrix, 0, sizeof(v4l2_scaling_matrix));
  
    static_assert(
        std::extent<decltype(v4l2_scaling_matrix.scaling_list_4x4)>() <=
                std::extent<decltype(pps->scaling_list4x4)>() &&
            std::extent<decltype(v4l2_scaling_matrix.scaling_list_4x4[0])>() <=
                std::extent<decltype(pps->scaling_list4x4[0])>() &&
            std::extent<decltype(v4l2_scaling_matrix.scaling_list_8x8)>() <=
                std::extent<decltype(pps->scaling_list8x8)>() &&
            std::extent<decltype(v4l2_scaling_matrix.scaling_list_8x8[0])>() <=
                std::extent<decltype(pps->scaling_list8x8[0])>(),
        "scaling_lists must be of correct size");
    static_assert(
        std::extent<decltype(v4l2_scaling_matrix.scaling_list_4x4)>() <=
                std::extent<decltype(sps->scaling_list4x4)>() &&
            std::extent<decltype(v4l2_scaling_matrix.scaling_list_4x4[0])>() <=
                std::extent<decltype(sps->scaling_list4x4[0])>() &&
            std::extent<decltype(v4l2_scaling_matrix.scaling_list_8x8)>() <=
                std::extent<decltype(sps->scaling_list8x8)>() &&
            std::extent<decltype(v4l2_scaling_matrix.scaling_list_8x8[0])>() <=
                std::extent<decltype(sps->scaling_list8x8[0])>(),
        "scaling_lists must be of correct size");
  
    const auto* scaling_list4x4 = &sps->scaling_list4x4[0];
    const auto* scaling_list8x8 = &sps->scaling_list8x8[0];
    if (pps->pic_scaling_matrix_present_flag) {
      scaling_list4x4 = &pps->scaling_list4x4[0];
      scaling_list8x8 = &pps->scaling_list8x8[0];
    }
  
    for (size_t i = 0; i < base::size(v4l2_scaling_matrix.scaling_list_4x4);
         ++i) {
      for (size_t j = 0; j < base::size(v4l2_scaling_matrix.scaling_list_4x4[i]);
           ++j) {
        v4l2_scaling_matrix.scaling_list_4x4[i][j] = scaling_list4x4[i][j];
      }
    }
    for (size_t i = 0; i < base::size(v4l2_scaling_matrix.scaling_list_8x8);
         ++i) {
      for (size_t j = 0; j < base::size(v4l2_scaling_matrix.scaling_list_8x8[i]);
           ++j) {
        v4l2_scaling_matrix.scaling_list_8x8[i][j] = scaling_list8x8[i][j];
      }
    }
  
    memset(&ctrl, 0, sizeof(ctrl));
    ctrl.id = V4L2_CID_MPEG_VIDEO_H264_SCALING_MATRIX;
    ctrl.size = sizeof(v4l2_scaling_matrix);
    ctrl.ptr = &v4l2_scaling_matrix;
    ctrls.push_back(ctrl);
  
    scoped_refptr<V4L2DecodeSurface> dec_surface =
        H264PictureToV4L2DecodeSurface(pic.get());
  
    struct v4l2_ext_controls ext_ctrls;
    memset(&ext_ctrls, 0, sizeof(ext_ctrls));
    ext_ctrls.count = ctrls.size();
    ext_ctrls.controls = &ctrls[0];
    dec_surface->PrepareSetCtrls(&ext_ctrls);
    if (device_->Ioctl(VIDIOC_S_EXT_CTRLS, &ext_ctrls) != 0) {
      VPLOGF(1) << "ioctl() failed: VIDIOC_S_EXT_CTRLS";
      return Status::kFail;
    }
  
    H264PictureListToDPBIndicesList(ref_pic_listp0,
                                    priv_->v4l2_decode_param.ref_pic_list_p0);
    H264PictureListToDPBIndicesList(ref_pic_listb0,
                                    priv_->v4l2_decode_param.ref_pic_list_b0);
    H264PictureListToDPBIndicesList(ref_pic_listb1,
                                    priv_->v4l2_decode_param.ref_pic_list_b1);
  
    std::vector<scoped_refptr<V4L2DecodeSurface>> ref_surfaces;
    H264DPBToV4L2DPB(dpb, &ref_surfaces);
    dec_surface->SetReferenceSurfaces(ref_surfaces);
  
    return Status::kOk;
  }
  
  H264Decoder::H264Accelerator::Status
  V4L2VideoDecoderDelegateH264Legacy::SubmitSlice(
      const H264PPS* pps,
      const H264SliceHeader* slice_hdr,
      const H264Picture::Vector& ref_pic_list0,
      const H264Picture::Vector& ref_pic_list1,
      scoped_refptr<H264Picture> pic,
      const uint8_t* data,
      size_t size,
      const std::vector<SubsampleEntry>& subsamples) {
    if (num_slices_ == priv_->kMaxSlices) {
      VLOGF(1) << "Over limit of supported slices per frame";
      return Status::kFail;
    }
  
    struct v4l2_ctrl_h264_slice_param& v4l2_slice_param =
        priv_->v4l2_slice_params[num_slices_++];
    memset(&v4l2_slice_param, 0, sizeof(v4l2_slice_param));
  
    v4l2_slice_param.size = size;
  #define SHDR_TO_V4L2SPARM(a) v4l2_slice_param.a = slice_hdr->a
    SHDR_TO_V4L2SPARM(header_bit_size);
    SHDR_TO_V4L2SPARM(first_mb_in_slice);
    SHDR_TO_V4L2SPARM(slice_type);
    SHDR_TO_V4L2SPARM(pic_parameter_set_id);
    SHDR_TO_V4L2SPARM(colour_plane_id);
    SHDR_TO_V4L2SPARM(frame_num);
    SHDR_TO_V4L2SPARM(idr_pic_id);
    SHDR_TO_V4L2SPARM(pic_order_cnt_lsb);
    SHDR_TO_V4L2SPARM(delta_pic_order_cnt_bottom);
    SHDR_TO_V4L2SPARM(delta_pic_order_cnt0);
    SHDR_TO_V4L2SPARM(delta_pic_order_cnt1);
    SHDR_TO_V4L2SPARM(redundant_pic_cnt);
    SHDR_TO_V4L2SPARM(dec_ref_pic_marking_bit_size);
    SHDR_TO_V4L2SPARM(cabac_init_idc);
    SHDR_TO_V4L2SPARM(slice_qp_delta);
    SHDR_TO_V4L2SPARM(slice_qs_delta);
    SHDR_TO_V4L2SPARM(disable_deblocking_filter_idc);
    SHDR_TO_V4L2SPARM(slice_alpha_c0_offset_div2);
    SHDR_TO_V4L2SPARM(slice_beta_offset_div2);
    SHDR_TO_V4L2SPARM(num_ref_idx_l0_active_minus1);
    SHDR_TO_V4L2SPARM(num_ref_idx_l1_active_minus1);
    SHDR_TO_V4L2SPARM(pic_order_cnt_bit_size);
  #undef SHDR_TO_V4L2SPARM
  
  #define SET_V4L2_SPARM_FLAG_IF(cond, flag) \
    v4l2_slice_param.flags |= ((slice_hdr->cond) ? (flag) : 0)
    SET_V4L2_SPARM_FLAG_IF(field_pic_flag, V4L2_SLICE_FLAG_FIELD_PIC);
    SET_V4L2_SPARM_FLAG_IF(bottom_field_flag, V4L2_SLICE_FLAG_BOTTOM_FIELD);
    SET_V4L2_SPARM_FLAG_IF(direct_spatial_mv_pred_flag,
                           V4L2_SLICE_FLAG_DIRECT_SPATIAL_MV_PRED);
    SET_V4L2_SPARM_FLAG_IF(sp_for_switch_flag, V4L2_SLICE_FLAG_SP_FOR_SWITCH);
  #undef SET_V4L2_SPARM_FLAG_IF
  
    struct v4l2_h264_pred_weight_table* pred_weight_table =
        &v4l2_slice_param.pred_weight_table;
  
    if (((slice_hdr->IsPSlice() || slice_hdr->IsSPSlice()) &&
         pps->weighted_pred_flag) ||
        (slice_hdr->IsBSlice() && pps->weighted_bipred_idc == 1)) {
      pred_weight_table->luma_log2_weight_denom =
          slice_hdr->luma_log2_weight_denom;
      pred_weight_table->chroma_log2_weight_denom =
          slice_hdr->chroma_log2_weight_denom;
  
      struct v4l2_h264_weight_factors* factorsl0 =
          &pred_weight_table->weight_factors[0];
  
      for (int i = 0; i < 32; ++i) {
        factorsl0->luma_weight[i] =
            slice_hdr->pred_weight_table_l0.luma_weight[i];
        factorsl0->luma_offset[i] =
            slice_hdr->pred_weight_table_l0.luma_offset[i];
  
        for (int j = 0; j < 2; ++j) {
          factorsl0->chroma_weight[i][j] =
              slice_hdr->pred_weight_table_l0.chroma_weight[i][j];
          factorsl0->chroma_offset[i][j] =
              slice_hdr->pred_weight_table_l0.chroma_offset[i][j];
        }
      }
  
      if (slice_hdr->IsBSlice()) {
        struct v4l2_h264_weight_factors* factorsl1 =
            &pred_weight_table->weight_factors[1];
  
        for (int i = 0; i < 32; ++i) {
          factorsl1->luma_weight[i] =
              slice_hdr->pred_weight_table_l1.luma_weight[i];
          factorsl1->luma_offset[i] =
              slice_hdr->pred_weight_table_l1.luma_offset[i];
  
          for (int j = 0; j < 2; ++j) {
            factorsl1->chroma_weight[i][j] =
                slice_hdr->pred_weight_table_l1.chroma_weight[i][j];
            factorsl1->chroma_offset[i][j] =
                slice_hdr->pred_weight_table_l1.chroma_offset[i][j];
          }
        }
      }
    }
  
    H264PictureListToDPBIndicesList(ref_pic_list0,
                                    v4l2_slice_param.ref_pic_list0);
    H264PictureListToDPBIndicesList(ref_pic_list1,
                                    v4l2_slice_param.ref_pic_list1);
  
    scoped_refptr<V4L2DecodeSurface> dec_surface =
        H264PictureToV4L2DecodeSurface(pic.get());
  
    priv_->v4l2_decode_param.nal_ref_idc = slice_hdr->nal_ref_idc;
  
    // TODO(posciak): Don't add start code back here, but have it passed from
    // the parser.
    size_t data_copy_size = size + 3;
    std::unique_ptr<uint8_t[]> data_copy(new uint8_t[data_copy_size]);
    memset(data_copy.get(), 0, data_copy_size);
    data_copy[2] = 0x01;
    memcpy(data_copy.get() + 3, data, size);
    return surface_handler_->SubmitSlice(dec_surface.get(), data_copy.get(),
                                         data_copy_size)
               ? Status::kOk
               : Status::kFail;
  }
  
  H264Decoder::H264Accelerator::Status
  V4L2VideoDecoderDelegateH264Legacy::SubmitDecode(
      scoped_refptr<H264Picture> pic) {
    scoped_refptr<V4L2DecodeSurface> dec_surface =
        H264PictureToV4L2DecodeSurface(pic.get());
  
    priv_->v4l2_decode_param.num_slices = num_slices_;
    priv_->v4l2_decode_param.idr_pic_flag = pic->idr;
    priv_->v4l2_decode_param.top_field_order_cnt = pic->top_field_order_cnt;
    priv_->v4l2_decode_param.bottom_field_order_cnt = pic->bottom_field_order_cnt;
  
    struct v4l2_ext_control ctrl;
    std::vector<struct v4l2_ext_control> ctrls;
  
    memset(&ctrl, 0, sizeof(ctrl));
    ctrl.id = V4L2_CID_MPEG_VIDEO_H264_SLICE_PARAM;
    ctrl.size = sizeof(priv_->v4l2_slice_params);
    ctrl.ptr = priv_->v4l2_slice_params;
    ctrls.push_back(ctrl);
  
    memset(&ctrl, 0, sizeof(ctrl));
    ctrl.id = V4L2_CID_MPEG_VIDEO_H264_DECODE_PARAM;
    ctrl.size = sizeof(priv_->v4l2_decode_param);
    ctrl.ptr = &priv_->v4l2_decode_param;
    ctrls.push_back(ctrl);
  
    struct v4l2_ext_controls ext_ctrls;
    memset(&ext_ctrls, 0, sizeof(ext_ctrls));
    ext_ctrls.count = ctrls.size();
    ext_ctrls.controls = &ctrls[0];
    dec_surface->PrepareSetCtrls(&ext_ctrls);
    if (device_->Ioctl(VIDIOC_S_EXT_CTRLS, &ext_ctrls) != 0) {
      VPLOGF(1) << "ioctl() failed: VIDIOC_S_EXT_CTRLS";
      return Status::kFail;
    }
  
    Reset();
  
    DVLOGF(4) << "Submitting decode for surface: " << dec_surface->ToString();
    surface_handler_->DecodeSurface(dec_surface);
    return Status::kOk;
  }
  
  bool V4L2VideoDecoderDelegateH264Legacy::OutputPicture(
      scoped_refptr<H264Picture> pic) {
    // TODO(crbug.com/647725): Insert correct color space.
    surface_handler_->SurfaceReady(H264PictureToV4L2DecodeSurface(pic.get()),
                                   pic->bitstream_id(), pic->visible_rect(),
                                   VideoColorSpace());
    return true;
  }
  
  void V4L2VideoDecoderDelegateH264Legacy::Reset() {
    num_slices_ = 0;
    memset(&priv_->v4l2_decode_param, 0, sizeof(priv_->v4l2_decode_param));
    memset(&priv_->v4l2_slice_params, 0, sizeof(priv_->v4l2_slice_params));
  }
  
  scoped_refptr<V4L2DecodeSurface>
  V4L2VideoDecoderDelegateH264Legacy::H264PictureToV4L2DecodeSurface(
      H264Picture* pic) {
    V4L2H264Picture* v4l2_pic = pic->AsV4L2H264Picture();
    CHECK(v4l2_pic);
    return v4l2_pic->dec_surface();
  }
  
  }  // namespace media
  