blob: 54b8924420544796f57ff64da630ec3c83aae62b [file] [log] [blame]
// Copyright 2018 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_h264_accelerator.h"
#include <linux/videodev2.h>
#include <type_traits>
#include "base/logging.h"
#include "base/stl_util.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 V4L2H264AcceleratorPrivate {
// 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(const scoped_refptr<V4L2DecodeSurface>& dec_surface)
: dec_surface_(dec_surface) {}
V4L2H264Picture* AsV4L2H264Picture() override { return this; }
scoped_refptr<V4L2DecodeSurface> dec_surface() { return dec_surface_; }
private:
~V4L2H264Picture() override {}
scoped_refptr<V4L2DecodeSurface> dec_surface_;
DISALLOW_COPY_AND_ASSIGN(V4L2H264Picture);
};
V4L2H264Accelerator::V4L2H264Accelerator(
V4L2DecodeSurfaceHandler* surface_handler,
V4L2Device* device)
: num_slices_(0),
surface_handler_(surface_handler),
device_(device),
priv_(std::make_unique<V4L2H264AcceleratorPrivate>()) {
DCHECK(surface_handler_);
}
V4L2H264Accelerator::~V4L2H264Accelerator() {}
scoped_refptr<H264Picture> V4L2H264Accelerator::CreateH264Picture() {
scoped_refptr<V4L2DecodeSurface> dec_surface =
surface_handler_->CreateSurface();
if (!dec_surface)
return nullptr;
return new V4L2H264Picture(dec_surface);
}
void V4L2H264Accelerator::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 scoped_refptr<H264Picture>& pic = src_pic_list[i];
dst_list[i] = pic ? pic->dpb_position : VIDEO_MAX_FRAME;
}
while (i < kDPBIndicesListSize)
dst_list[i++] = VIDEO_MAX_FRAME;
}
void V4L2H264Accelerator::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;
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 V4L2H264Accelerator::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 V4L2H264Accelerator::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, data_copy.get(),
data_copy_size)
? Status::kOk
: Status::kFail;
}
H264Decoder::H264Accelerator::Status V4L2H264Accelerator::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 V4L2H264Accelerator::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 V4L2H264Accelerator::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>
V4L2H264Accelerator::H264PictureToV4L2DecodeSurface(H264Picture* pic) {
V4L2H264Picture* v4l2_pic = pic->AsV4L2H264Picture();
CHECK(v4l2_pic);
return v4l2_pic->dec_surface();
}
} // namespace media