blob: 00aa0a7fb2cc1b041390522e67eef0ef97bb1547 [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_vp9_accelerator.h"
#include <type_traits>
#include <linux/videodev2.h>
#include <string.h>
#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"
#include "media/gpu/vp9_picture.h"
namespace media {
namespace {
void FillV4L2VP9LoopFilterParams(
const Vp9LoopFilterParams& vp9_lf_params,
struct v4l2_vp9_loop_filter_params* v4l2_lf_params) {
#define SET_LF_PARAMS_FLAG_IF(cond, flag) \
v4l2_lf_params->flags |= ((vp9_lf_params.cond) ? (flag) : 0)
SET_LF_PARAMS_FLAG_IF(delta_enabled, V4L2_VP9_LOOP_FLTR_FLAG_DELTA_ENABLED);
SET_LF_PARAMS_FLAG_IF(delta_update, V4L2_VP9_LOOP_FLTR_FLAG_DELTA_UPDATE);
#undef SET_LF_PARAMS_FLAG_IF
v4l2_lf_params->level = vp9_lf_params.level;
v4l2_lf_params->sharpness = vp9_lf_params.sharpness;
SafeArrayMemcpy(v4l2_lf_params->deltas, vp9_lf_params.ref_deltas);
SafeArrayMemcpy(v4l2_lf_params->mode_deltas, vp9_lf_params.mode_deltas);
SafeArrayMemcpy(v4l2_lf_params->lvl_lookup, vp9_lf_params.lvl);
}
void FillV4L2VP9QuantizationParams(
const Vp9QuantizationParams& vp9_quant_params,
struct v4l2_vp9_quantization_params* v4l2_q_params) {
#define SET_Q_PARAMS_FLAG_IF(cond, flag) \
v4l2_q_params->flags |= ((vp9_quant_params.cond) ? (flag) : 0)
SET_Q_PARAMS_FLAG_IF(IsLossless(), V4L2_VP9_QUANT_PARAMS_FLAG_LOSSLESS);
#undef SET_Q_PARAMS_FLAG_IF
#define Q_PARAMS_TO_V4L2_Q_PARAMS(a) v4l2_q_params->a = vp9_quant_params.a
Q_PARAMS_TO_V4L2_Q_PARAMS(base_q_idx);
Q_PARAMS_TO_V4L2_Q_PARAMS(delta_q_y_dc);
Q_PARAMS_TO_V4L2_Q_PARAMS(delta_q_uv_dc);
Q_PARAMS_TO_V4L2_Q_PARAMS(delta_q_uv_ac);
#undef Q_PARAMS_TO_V4L2_Q_PARAMS
}
void FillV4L2VP9SegmentationParams(
const Vp9SegmentationParams& vp9_segm_params,
struct v4l2_vp9_segmentation_params* v4l2_segm_params) {
#define SET_SEG_PARAMS_FLAG_IF(cond, flag) \
v4l2_segm_params->flags |= ((vp9_segm_params.cond) ? (flag) : 0)
SET_SEG_PARAMS_FLAG_IF(enabled, V4L2_VP9_SGMNT_PARAM_FLAG_ENABLED);
SET_SEG_PARAMS_FLAG_IF(update_map, V4L2_VP9_SGMNT_PARAM_FLAG_UPDATE_MAP);
SET_SEG_PARAMS_FLAG_IF(temporal_update,
V4L2_VP9_SGMNT_PARAM_FLAG_TEMPORAL_UPDATE);
SET_SEG_PARAMS_FLAG_IF(update_data, V4L2_VP9_SGMNT_PARAM_FLAG_UPDATE_DATA);
SET_SEG_PARAMS_FLAG_IF(abs_or_delta_update,
V4L2_VP9_SGMNT_PARAM_FLAG_ABS_OR_DELTA_UPDATE);
#undef SET_SEG_PARAMS_FLAG_IF
SafeArrayMemcpy(v4l2_segm_params->tree_probs, vp9_segm_params.tree_probs);
SafeArrayMemcpy(v4l2_segm_params->pred_probs, vp9_segm_params.pred_probs);
SafeArrayMemcpy(v4l2_segm_params->feature_data, vp9_segm_params.feature_data);
static_assert(
std::extent<decltype(v4l2_segm_params->feature_enabled)>() ==
std::extent<decltype(vp9_segm_params.feature_enabled)>() &&
std::extent<decltype(v4l2_segm_params->feature_enabled[0])>() ==
std::extent<decltype(vp9_segm_params.feature_enabled[0])>(),
"feature_enabled arrays must be of same size");
for (size_t i = 0; i < base::size(v4l2_segm_params->feature_enabled); ++i) {
for (size_t j = 0; j < base::size(v4l2_segm_params->feature_enabled[i]);
++j) {
v4l2_segm_params->feature_enabled[i][j] =
vp9_segm_params.feature_enabled[i][j];
}
}
}
void FillV4L2Vp9EntropyContext(const Vp9FrameContext& vp9_frame_ctx,
struct v4l2_vp9_entropy_ctx* v4l2_entropy_ctx) {
#define ARRAY_MEMCPY_CHECKED_FRM_CTX_TO_V4L2_ENTR(a) \
SafeArrayMemcpy(v4l2_entropy_ctx->a, vp9_frame_ctx.a)
ARRAY_MEMCPY_CHECKED_FRM_CTX_TO_V4L2_ENTR(tx_probs_8x8);
ARRAY_MEMCPY_CHECKED_FRM_CTX_TO_V4L2_ENTR(tx_probs_16x16);
ARRAY_MEMCPY_CHECKED_FRM_CTX_TO_V4L2_ENTR(tx_probs_32x32);
ARRAY_MEMCPY_CHECKED_FRM_CTX_TO_V4L2_ENTR(coef_probs);
ARRAY_MEMCPY_CHECKED_FRM_CTX_TO_V4L2_ENTR(skip_prob);
ARRAY_MEMCPY_CHECKED_FRM_CTX_TO_V4L2_ENTR(inter_mode_probs);
ARRAY_MEMCPY_CHECKED_FRM_CTX_TO_V4L2_ENTR(interp_filter_probs);
ARRAY_MEMCPY_CHECKED_FRM_CTX_TO_V4L2_ENTR(is_inter_prob);
ARRAY_MEMCPY_CHECKED_FRM_CTX_TO_V4L2_ENTR(comp_mode_prob);
ARRAY_MEMCPY_CHECKED_FRM_CTX_TO_V4L2_ENTR(single_ref_prob);
ARRAY_MEMCPY_CHECKED_FRM_CTX_TO_V4L2_ENTR(comp_ref_prob);
ARRAY_MEMCPY_CHECKED_FRM_CTX_TO_V4L2_ENTR(y_mode_probs);
ARRAY_MEMCPY_CHECKED_FRM_CTX_TO_V4L2_ENTR(uv_mode_probs);
ARRAY_MEMCPY_CHECKED_FRM_CTX_TO_V4L2_ENTR(partition_probs);
ARRAY_MEMCPY_CHECKED_FRM_CTX_TO_V4L2_ENTR(mv_joint_probs);
ARRAY_MEMCPY_CHECKED_FRM_CTX_TO_V4L2_ENTR(mv_sign_prob);
ARRAY_MEMCPY_CHECKED_FRM_CTX_TO_V4L2_ENTR(mv_class_probs);
ARRAY_MEMCPY_CHECKED_FRM_CTX_TO_V4L2_ENTR(mv_class0_bit_prob);
ARRAY_MEMCPY_CHECKED_FRM_CTX_TO_V4L2_ENTR(mv_bits_prob);
ARRAY_MEMCPY_CHECKED_FRM_CTX_TO_V4L2_ENTR(mv_class0_fr_probs);
ARRAY_MEMCPY_CHECKED_FRM_CTX_TO_V4L2_ENTR(mv_fr_probs);
ARRAY_MEMCPY_CHECKED_FRM_CTX_TO_V4L2_ENTR(mv_class0_hp_prob);
ARRAY_MEMCPY_CHECKED_FRM_CTX_TO_V4L2_ENTR(mv_hp_prob);
#undef ARRAY_MEMCPY_CHECKED_FRM_CTX_TO_V4L2_ENTR
}
void FillVp9FrameContext(struct v4l2_vp9_entropy_ctx& v4l2_entropy_ctx,
Vp9FrameContext* vp9_frame_ctx) {
#define ARRAY_MEMCPY_CHECKED_V4L2_ENTR_TO_FRM_CTX(a) \
SafeArrayMemcpy(vp9_frame_ctx->a, v4l2_entropy_ctx.a)
ARRAY_MEMCPY_CHECKED_V4L2_ENTR_TO_FRM_CTX(tx_probs_8x8);
ARRAY_MEMCPY_CHECKED_V4L2_ENTR_TO_FRM_CTX(tx_probs_16x16);
ARRAY_MEMCPY_CHECKED_V4L2_ENTR_TO_FRM_CTX(tx_probs_32x32);
ARRAY_MEMCPY_CHECKED_V4L2_ENTR_TO_FRM_CTX(coef_probs);
ARRAY_MEMCPY_CHECKED_V4L2_ENTR_TO_FRM_CTX(skip_prob);
ARRAY_MEMCPY_CHECKED_V4L2_ENTR_TO_FRM_CTX(inter_mode_probs);
ARRAY_MEMCPY_CHECKED_V4L2_ENTR_TO_FRM_CTX(interp_filter_probs);
ARRAY_MEMCPY_CHECKED_V4L2_ENTR_TO_FRM_CTX(is_inter_prob);
ARRAY_MEMCPY_CHECKED_V4L2_ENTR_TO_FRM_CTX(comp_mode_prob);
ARRAY_MEMCPY_CHECKED_V4L2_ENTR_TO_FRM_CTX(single_ref_prob);
ARRAY_MEMCPY_CHECKED_V4L2_ENTR_TO_FRM_CTX(comp_ref_prob);
ARRAY_MEMCPY_CHECKED_V4L2_ENTR_TO_FRM_CTX(y_mode_probs);
ARRAY_MEMCPY_CHECKED_V4L2_ENTR_TO_FRM_CTX(uv_mode_probs);
ARRAY_MEMCPY_CHECKED_V4L2_ENTR_TO_FRM_CTX(partition_probs);
ARRAY_MEMCPY_CHECKED_V4L2_ENTR_TO_FRM_CTX(mv_joint_probs);
ARRAY_MEMCPY_CHECKED_V4L2_ENTR_TO_FRM_CTX(mv_sign_prob);
ARRAY_MEMCPY_CHECKED_V4L2_ENTR_TO_FRM_CTX(mv_class_probs);
ARRAY_MEMCPY_CHECKED_V4L2_ENTR_TO_FRM_CTX(mv_class0_bit_prob);
ARRAY_MEMCPY_CHECKED_V4L2_ENTR_TO_FRM_CTX(mv_bits_prob);
ARRAY_MEMCPY_CHECKED_V4L2_ENTR_TO_FRM_CTX(mv_class0_fr_probs);
ARRAY_MEMCPY_CHECKED_V4L2_ENTR_TO_FRM_CTX(mv_fr_probs);
ARRAY_MEMCPY_CHECKED_V4L2_ENTR_TO_FRM_CTX(mv_class0_hp_prob);
ARRAY_MEMCPY_CHECKED_V4L2_ENTR_TO_FRM_CTX(mv_hp_prob);
#undef ARRAY_MEMCPY_CHECKED_V4L2_ENTR_TO_FRM_CTX
}
} // namespace
class V4L2VP9Picture : public VP9Picture {
public:
explicit V4L2VP9Picture(scoped_refptr<V4L2DecodeSurface> dec_surface)
: dec_surface_(std::move(dec_surface)) {}
V4L2VP9Picture* AsV4L2VP9Picture() override { return this; }
scoped_refptr<V4L2DecodeSurface> dec_surface() { return dec_surface_; }
private:
~V4L2VP9Picture() override {}
scoped_refptr<VP9Picture> CreateDuplicate() override {
return new V4L2VP9Picture(dec_surface_);
}
scoped_refptr<V4L2DecodeSurface> dec_surface_;
DISALLOW_COPY_AND_ASSIGN(V4L2VP9Picture);
};
V4L2VP9Accelerator::V4L2VP9Accelerator(
V4L2DecodeSurfaceHandler* surface_handler,
V4L2Device* device)
: surface_handler_(surface_handler), device_(device) {
DCHECK(surface_handler_);
struct v4l2_queryctrl query_ctrl;
memset(&query_ctrl, 0, sizeof(query_ctrl));
query_ctrl.id = V4L2_CID_MPEG_VIDEO_VP9_ENTROPY;
device_needs_frame_context_ =
(device_->Ioctl(VIDIOC_QUERYCTRL, &query_ctrl) == 0);
DVLOG_IF(1, device_needs_frame_context_)
<< "Device requires frame context parsing";
}
V4L2VP9Accelerator::~V4L2VP9Accelerator() {}
scoped_refptr<VP9Picture> V4L2VP9Accelerator::CreateVP9Picture() {
scoped_refptr<V4L2DecodeSurface> dec_surface =
surface_handler_->CreateSurface();
if (!dec_surface)
return nullptr;
return new V4L2VP9Picture(std::move(dec_surface));
}
bool V4L2VP9Accelerator::SubmitDecode(scoped_refptr<VP9Picture> pic,
const Vp9SegmentationParams& segm_params,
const Vp9LoopFilterParams& lf_params,
const Vp9ReferenceFrameVector& ref_frames,
const base::Closure& done_cb) {
const Vp9FrameHeader* frame_hdr = pic->frame_hdr.get();
DCHECK(frame_hdr);
struct v4l2_ctrl_vp9_frame_hdr v4l2_frame_hdr;
memset(&v4l2_frame_hdr, 0, sizeof(v4l2_frame_hdr));
#define FHDR_TO_V4L2_FHDR(a) v4l2_frame_hdr.a = frame_hdr->a
FHDR_TO_V4L2_FHDR(profile);
FHDR_TO_V4L2_FHDR(frame_type);
FHDR_TO_V4L2_FHDR(bit_depth);
FHDR_TO_V4L2_FHDR(color_range);
FHDR_TO_V4L2_FHDR(subsampling_x);
FHDR_TO_V4L2_FHDR(subsampling_y);
FHDR_TO_V4L2_FHDR(frame_width);
FHDR_TO_V4L2_FHDR(frame_height);
FHDR_TO_V4L2_FHDR(render_width);
FHDR_TO_V4L2_FHDR(render_height);
FHDR_TO_V4L2_FHDR(reset_frame_context);
FHDR_TO_V4L2_FHDR(interpolation_filter);
FHDR_TO_V4L2_FHDR(frame_context_idx);
FHDR_TO_V4L2_FHDR(tile_cols_log2);
FHDR_TO_V4L2_FHDR(tile_rows_log2);
FHDR_TO_V4L2_FHDR(header_size_in_bytes);
#undef FHDR_TO_V4L2_FHDR
v4l2_frame_hdr.color_space = static_cast<uint8_t>(frame_hdr->color_space);
FillV4L2VP9QuantizationParams(frame_hdr->quant_params,
&v4l2_frame_hdr.quant_params);
#define SET_V4L2_FRM_HDR_FLAG_IF(cond, flag) \
v4l2_frame_hdr.flags |= ((frame_hdr->cond) ? (flag) : 0)
SET_V4L2_FRM_HDR_FLAG_IF(show_frame, V4L2_VP9_FRAME_HDR_FLAG_SHOW_FRAME);
SET_V4L2_FRM_HDR_FLAG_IF(error_resilient_mode,
V4L2_VP9_FRAME_HDR_FLAG_ERR_RES);
SET_V4L2_FRM_HDR_FLAG_IF(intra_only, V4L2_VP9_FRAME_HDR_FLAG_FRAME_INTRA);
SET_V4L2_FRM_HDR_FLAG_IF(allow_high_precision_mv,
V4L2_VP9_FRAME_HDR_ALLOW_HIGH_PREC_MV);
SET_V4L2_FRM_HDR_FLAG_IF(refresh_frame_context,
V4L2_VP9_FRAME_HDR_REFRESH_FRAME_CTX);
SET_V4L2_FRM_HDR_FLAG_IF(frame_parallel_decoding_mode,
V4L2_VP9_FRAME_HDR_PARALLEL_DEC_MODE);
#undef SET_V4L2_FRM_HDR_FLAG_IF
FillV4L2VP9LoopFilterParams(lf_params, &v4l2_frame_hdr.lf_params);
FillV4L2VP9SegmentationParams(segm_params, &v4l2_frame_hdr.sgmnt_params);
std::vector<struct v4l2_ext_control> ctrls;
struct v4l2_ext_control ctrl;
memset(&ctrl, 0, sizeof(ctrl));
ctrl.id = V4L2_CID_MPEG_VIDEO_VP9_FRAME_HDR;
ctrl.size = sizeof(v4l2_frame_hdr);
ctrl.p_vp9_frame_hdr = &v4l2_frame_hdr;
ctrls.push_back(ctrl);
struct v4l2_ctrl_vp9_decode_param v4l2_decode_param;
memset(&v4l2_decode_param, 0, sizeof(v4l2_decode_param));
DCHECK_EQ(kVp9NumRefFrames, base::size(v4l2_decode_param.ref_frames));
std::vector<scoped_refptr<V4L2DecodeSurface>> ref_surfaces;
for (size_t i = 0; i < kVp9NumRefFrames; ++i) {
auto ref_pic = ref_frames.GetFrame(i);
if (ref_pic) {
scoped_refptr<V4L2DecodeSurface> ref_surface =
VP9PictureToV4L2DecodeSurface(ref_pic.get());
v4l2_decode_param.ref_frames[i] = ref_surface->GetReferenceID();
ref_surfaces.push_back(ref_surface);
} else {
v4l2_decode_param.ref_frames[i] = VIDEO_MAX_FRAME;
}
}
static_assert(std::extent<decltype(v4l2_decode_param.active_ref_frames)>() ==
std::extent<decltype(frame_hdr->ref_frame_idx)>(),
"active reference frame array sizes mismatch");
for (size_t i = 0; i < base::size(frame_hdr->ref_frame_idx); ++i) {
uint8_t idx = frame_hdr->ref_frame_idx[i];
if (idx >= kVp9NumRefFrames)
return false;
struct v4l2_vp9_reference_frame* v4l2_ref_frame =
&v4l2_decode_param.active_ref_frames[i];
scoped_refptr<VP9Picture> ref_pic = ref_frames.GetFrame(idx);
if (ref_pic) {
scoped_refptr<V4L2DecodeSurface> ref_surface =
VP9PictureToV4L2DecodeSurface(ref_pic.get());
v4l2_ref_frame->buf_index = ref_surface->GetReferenceID();
#define REF_TO_V4L2_REF(a) v4l2_ref_frame->a = ref_pic->frame_hdr->a
REF_TO_V4L2_REF(frame_width);
REF_TO_V4L2_REF(frame_height);
REF_TO_V4L2_REF(bit_depth);
REF_TO_V4L2_REF(subsampling_x);
REF_TO_V4L2_REF(subsampling_y);
#undef REF_TO_V4L2_REF
} else {
v4l2_ref_frame->buf_index = VIDEO_MAX_FRAME;
}
}
memset(&ctrl, 0, sizeof(ctrl));
ctrl.id = V4L2_CID_MPEG_VIDEO_VP9_DECODE_PARAM;
ctrl.size = sizeof(v4l2_decode_param);
ctrl.p_vp9_decode_param = &v4l2_decode_param;
ctrls.push_back(ctrl);
// Defined outside of the if() clause below as it must remain valid until
// the call to SubmitExtControls().
struct v4l2_ctrl_vp9_entropy v4l2_entropy;
if (device_needs_frame_context_) {
memset(&v4l2_entropy, 0, sizeof(v4l2_entropy));
FillV4L2Vp9EntropyContext(frame_hdr->initial_frame_context,
&v4l2_entropy.initial_entropy_ctx);
FillV4L2Vp9EntropyContext(frame_hdr->frame_context,
&v4l2_entropy.current_entropy_ctx);
v4l2_entropy.tx_mode = frame_hdr->compressed_header.tx_mode;
v4l2_entropy.reference_mode = frame_hdr->compressed_header.reference_mode;
memset(&ctrl, 0, sizeof(ctrl));
ctrl.id = V4L2_CID_MPEG_VIDEO_VP9_ENTROPY;
ctrl.size = sizeof(v4l2_entropy);
ctrl.p_vp9_entropy = &v4l2_entropy;
ctrls.push_back(ctrl);
}
scoped_refptr<V4L2DecodeSurface> dec_surface =
VP9PictureToV4L2DecodeSurface(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 false;
}
dec_surface->SetReferenceSurfaces(ref_surfaces);
dec_surface->SetDecodeDoneCallback(done_cb);
if (!surface_handler_->SubmitSlice(dec_surface, frame_hdr->data,
frame_hdr->frame_size))
return false;
DVLOGF(4) << "Submitting decode for surface: " << dec_surface->ToString();
surface_handler_->DecodeSurface(dec_surface);
return true;
}
bool V4L2VP9Accelerator::OutputPicture(scoped_refptr<VP9Picture> pic) {
// TODO(crbug.com/647725): Insert correct color space.
surface_handler_->SurfaceReady(VP9PictureToV4L2DecodeSurface(pic.get()),
pic->bitstream_id(), pic->visible_rect(),
VideoColorSpace());
return true;
}
bool V4L2VP9Accelerator::GetFrameContext(scoped_refptr<VP9Picture> pic,
Vp9FrameContext* frame_ctx) {
struct v4l2_ctrl_vp9_entropy v4l2_entropy;
memset(&v4l2_entropy, 0, sizeof(v4l2_entropy));
struct v4l2_ext_control ctrl;
memset(&ctrl, 0, sizeof(ctrl));
ctrl.id = V4L2_CID_MPEG_VIDEO_VP9_ENTROPY;
ctrl.size = sizeof(v4l2_entropy);
ctrl.p_vp9_entropy = &v4l2_entropy;
scoped_refptr<V4L2DecodeSurface> dec_surface =
VP9PictureToV4L2DecodeSurface(pic.get());
struct v4l2_ext_controls ext_ctrls;
memset(&ext_ctrls, 0, sizeof(ext_ctrls));
ext_ctrls.count = 1;
ext_ctrls.controls = &ctrl;
dec_surface->PrepareSetCtrls(&ext_ctrls);
if (device_->Ioctl(VIDIOC_G_EXT_CTRLS, &ext_ctrls) != 0) {
VPLOGF(1) << "ioctl() failed: VIDIOC_G_EXT_CTRLS";
return false;
}
FillVp9FrameContext(v4l2_entropy.current_entropy_ctx, frame_ctx);
return true;
}
bool V4L2VP9Accelerator::IsFrameContextRequired() const {
return device_needs_frame_context_;
}
scoped_refptr<V4L2DecodeSurface>
V4L2VP9Accelerator::VP9PictureToV4L2DecodeSurface(VP9Picture* pic) {
V4L2VP9Picture* v4l2_pic = pic->AsV4L2VP9Picture();
CHECK(v4l2_pic);
return v4l2_pic->dec_surface();
}
} // namespace media