blob: 31c3f5a7fe388a8c3a245478db36597b945f8567 [file] [log] [blame]
// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "media/gpu/v4l2/test/av1_decoder.h"
#include <linux/media/av1-ctrls.h>
#include "base/logging.h"
#include "base/memory/ptr_util.h"
#include "base/notreached.h"
#include "media/base/video_types.h"
#include "media/filters/ivf_parser.h"
#include "media/gpu/macros.h"
#include "media/gpu/v4l2/test/upstream_pix_fmt.h"
#include "third_party/libgav1/src/src/warp_prediction.h"
namespace media {
namespace v4l2_test {
namespace {
constexpr uint32_t kDriverCodecFourcc = V4L2_PIX_FMT_AV1_FRAME;
constexpr uint32_t kNumberOfBuffersInCaptureQueue = 10;
static_assert(kNumberOfBuffersInCaptureQueue <= 16,
"Too many CAPTURE buffers are used. The number of CAPTURE "
"buffers is currently assumed to be no larger than 16.");
// TODO(stevecho): Remove this provision when av1-ctrls.h includes linux/bits.h.
#ifndef BIT
#define BIT(nr) (1U << (nr))
#endif
inline void conditionally_set_flags(__u8* flags,
const bool condition,
const __u8 mask) {
*flags |= (condition ? mask : 0);
}
inline void conditionally_set_u32_flags(__u32* flags,
const bool condition,
const __u32 mask) {
*flags |= (condition ? mask : 0);
}
// The resolution encoded in the bitstream is required for queue creation. Note
// that parsing ivf file and parsing the first frame using libgav1 parser happen
// again later in the code. This is intentionally duplicated.
const gfx::Size GetResolutionFromBitstream(
const base::MemoryMappedFile& stream) {
media::IvfParser ivf_parser{};
media::IvfFileHeader ivf_file_header{};
if (!ivf_parser.Initialize(stream.data(), stream.length(), &ivf_file_header))
LOG(FATAL) << "Couldn't initialize IVF parser.";
IvfFrameHeader ivf_frame_header{};
const uint8_t* ivf_frame_data = nullptr;
if (!ivf_parser.ParseNextFrame(&ivf_frame_header, &ivf_frame_data))
LOG(FATAL) << "Failed to parse the first frame with IVF parser.";
VLOG(2) << "Ivf file header: " << ivf_file_header.width << " x "
<< ivf_file_header.height;
libgav1::InternalFrameBufferList buffer_list;
libgav1::BufferPool buffer_pool(libgav1::OnInternalFrameBufferSizeChanged,
libgav1::GetInternalFrameBuffer,
libgav1::ReleaseInternalFrameBuffer,
&buffer_list);
libgav1::DecoderState decoder_state;
libgav1::ObuParser av1_parser(ivf_frame_data, ivf_frame_header.frame_size, 0,
&buffer_pool, &decoder_state);
libgav1::RefCountedBufferPtr first_frame;
if (!av1_parser.HasData())
LOG(FATAL) << "Libgav1 parser doesn't have any data to parse.";
if (av1_parser.ParseOneFrame(&first_frame) != libgav1::kStatusOk)
LOG(FATAL) << "Failed to parse the first frame using libgav1 parser.";
LOG(INFO) << "Frame header: " << av1_parser.frame_header().width << " x "
<< av1_parser.frame_header().height;
return gfx::Size(av1_parser.frame_header().width,
av1_parser.frame_header().height);
}
// Section 5.5. Sequence header OBU syntax in the AV1 spec.
// https://aomediacodec.github.io/av1-spec/av1-spec.pdf
void FillSequenceParams(
struct v4l2_ctrl_av1_sequence* v4l2_seq_params,
const absl::optional<libgav1::ObuSequenceHeader>& seq_header) {
conditionally_set_u32_flags(&v4l2_seq_params->flags,
seq_header->still_picture,
V4L2_AV1_SEQUENCE_FLAG_STILL_PICTURE);
conditionally_set_u32_flags(&v4l2_seq_params->flags,
seq_header->use_128x128_superblock,
V4L2_AV1_SEQUENCE_FLAG_USE_128X128_SUPERBLOCK);
conditionally_set_u32_flags(&v4l2_seq_params->flags,
seq_header->enable_filter_intra,
V4L2_AV1_SEQUENCE_FLAG_ENABLE_FILTER_INTRA);
conditionally_set_u32_flags(&v4l2_seq_params->flags,
seq_header->enable_intra_edge_filter,
V4L2_AV1_SEQUENCE_FLAG_ENABLE_INTRA_EDGE_FILTER);
conditionally_set_u32_flags(
&v4l2_seq_params->flags, seq_header->enable_interintra_compound,
V4L2_AV1_SEQUENCE_FLAG_ENABLE_INTERINTRA_COMPOUND);
conditionally_set_u32_flags(&v4l2_seq_params->flags,
seq_header->enable_masked_compound,
V4L2_AV1_SEQUENCE_FLAG_ENABLE_MASKED_COMPOUND);
conditionally_set_u32_flags(&v4l2_seq_params->flags,
seq_header->enable_warped_motion,
V4L2_AV1_SEQUENCE_FLAG_ENABLE_WARPED_MOTION);
conditionally_set_u32_flags(&v4l2_seq_params->flags,
seq_header->enable_dual_filter,
V4L2_AV1_SEQUENCE_FLAG_ENABLE_DUAL_FILTER);
conditionally_set_u32_flags(&v4l2_seq_params->flags,
seq_header->enable_order_hint,
V4L2_AV1_SEQUENCE_FLAG_ENABLE_ORDER_HINT);
conditionally_set_u32_flags(&v4l2_seq_params->flags,
seq_header->enable_jnt_comp,
V4L2_AV1_SEQUENCE_FLAG_ENABLE_JNT_COMP);
conditionally_set_u32_flags(&v4l2_seq_params->flags,
seq_header->enable_ref_frame_mvs,
V4L2_AV1_SEQUENCE_FLAG_ENABLE_REF_FRAME_MVS);
conditionally_set_u32_flags(&v4l2_seq_params->flags,
seq_header->enable_superres,
V4L2_AV1_SEQUENCE_FLAG_ENABLE_SUPERRES);
conditionally_set_u32_flags(&v4l2_seq_params->flags, seq_header->enable_cdef,
V4L2_AV1_SEQUENCE_FLAG_ENABLE_CDEF);
conditionally_set_u32_flags(&v4l2_seq_params->flags,
seq_header->enable_restoration,
V4L2_AV1_SEQUENCE_FLAG_ENABLE_RESTORATION);
conditionally_set_u32_flags(&v4l2_seq_params->flags,
seq_header->color_config.is_monochrome,
V4L2_AV1_SEQUENCE_FLAG_MONO_CHROME);
conditionally_set_u32_flags(&v4l2_seq_params->flags,
seq_header->color_config.color_range,
V4L2_AV1_SEQUENCE_FLAG_COLOR_RANGE);
conditionally_set_u32_flags(&v4l2_seq_params->flags,
seq_header->color_config.subsampling_x,
V4L2_AV1_SEQUENCE_FLAG_SUBSAMPLING_X);
conditionally_set_u32_flags(&v4l2_seq_params->flags,
seq_header->color_config.subsampling_y,
V4L2_AV1_SEQUENCE_FLAG_SUBSAMPLING_Y);
conditionally_set_u32_flags(&v4l2_seq_params->flags,
seq_header->film_grain_params_present,
V4L2_AV1_SEQUENCE_FLAG_FILM_GRAIN_PARAMS_PRESENT);
conditionally_set_u32_flags(&v4l2_seq_params->flags,
seq_header->color_config.separate_uv_delta_q,
V4L2_AV1_SEQUENCE_FLAG_SEPARATE_UV_DELTA_Q);
v4l2_seq_params->seq_profile = seq_header->profile;
v4l2_seq_params->order_hint_bits = seq_header->order_hint_bits;
v4l2_seq_params->bit_depth = seq_header->color_config.bitdepth;
v4l2_seq_params->max_frame_width_minus_1 = seq_header->max_frame_width - 1;
v4l2_seq_params->max_frame_height_minus_1 = seq_header->max_frame_height - 1;
}
// Section 5.9.11. Loop filter params syntax.
// Note that |update_ref_delta| and |update_mode_delta| flags in the spec
// are not needed for V4L2 AV1 API.
void FillLoopFilterParams(struct v4l2_av1_loop_filter* v4l2_lf,
const libgav1::LoopFilter& lf) {
conditionally_set_flags(&v4l2_lf->flags, lf.delta_enabled,
V4L2_AV1_LOOP_FILTER_FLAG_DELTA_ENABLED);
conditionally_set_flags(&v4l2_lf->flags, lf.delta_update,
V4L2_AV1_LOOP_FILTER_FLAG_DELTA_UPDATE);
static_assert(std::size(decltype(v4l2_lf->level){}) == libgav1::kFrameLfCount,
"Invalid size of loop filter level (strength) array");
for (size_t i = 0; i < libgav1::kFrameLfCount; i++)
v4l2_lf->level[i] = base::checked_cast<__u8>(lf.level[i]);
v4l2_lf->sharpness = lf.sharpness;
static_assert(std::size(decltype(v4l2_lf->ref_deltas){}) ==
libgav1::kNumReferenceFrameTypes,
"Invalid size of ref deltas array");
for (size_t i = 0; i < libgav1::kNumReferenceFrameTypes; i++)
v4l2_lf->ref_deltas[i] = lf.ref_deltas[i];
static_assert(std::size(decltype(v4l2_lf->mode_deltas){}) ==
libgav1::kLoopFilterMaxModeDeltas,
"Invalid size of mode deltas array");
for (size_t i = 0; i < libgav1::kLoopFilterMaxModeDeltas; i++)
v4l2_lf->mode_deltas[i] = lf.mode_deltas[i];
}
// Section 5.9.18. Loop filter delta parameters syntax.
// Note that |delta_lf_res| in |v4l2_av1_loop_filter| corresponds to
// |delta_lf.scale| in the frame header defined in libgav1.
void FillLoopFilterDeltaParams(struct v4l2_av1_loop_filter* v4l2_lf,
const libgav1::Delta& delta_lf) {
conditionally_set_flags(&v4l2_lf->flags, delta_lf.present,
V4L2_AV1_LOOP_FILTER_FLAG_DELTA_LF_PRESENT);
conditionally_set_flags(&v4l2_lf->flags, delta_lf.multi,
V4L2_AV1_LOOP_FILTER_FLAG_DELTA_LF_MULTI);
v4l2_lf->delta_lf_res = delta_lf.scale;
}
// Section 5.9.12. Quantization params syntax
void FillQuantizationParams(struct v4l2_av1_quantization* v4l2_quant,
const libgav1::QuantizerParameters& quant) {
conditionally_set_flags(&v4l2_quant->flags, quant.use_matrix,
V4L2_AV1_QUANTIZATION_FLAG_USING_QMATRIX);
v4l2_quant->base_q_idx = quant.base_index;
// Note that quant.delta_ac[0] is useless as it is always 0 according to
// libgav1.
v4l2_quant->delta_q_y_dc = quant.delta_dc[0];
v4l2_quant->delta_q_u_dc = quant.delta_dc[1];
v4l2_quant->delta_q_u_ac = quant.delta_ac[1];
v4l2_quant->delta_q_v_dc = quant.delta_dc[2];
v4l2_quant->delta_q_v_ac = quant.delta_ac[2];
if (!quant.use_matrix)
return;
v4l2_quant->qm_y = base::checked_cast<uint8_t>(quant.matrix_level[0]);
v4l2_quant->qm_u = base::checked_cast<uint8_t>(quant.matrix_level[1]);
v4l2_quant->qm_v = base::checked_cast<uint8_t>(quant.matrix_level[2]);
}
// Section 5.9.17. Quantizer index delta parameters syntax
void FillQuantizerIndexDeltaParams(
struct v4l2_av1_quantization* v4l2_quant,
const absl::optional<libgav1::ObuSequenceHeader>& seq_header,
const libgav1::ObuFrameHeader& frm_header) {
// |diff_uv_delta| in the spec doesn't exist in libgav1,
// because libgav1 infers it using the following logic.
const bool diff_uv_delta = (frm_header.quantizer.base_index != 0) &&
(!seq_header->color_config.is_monochrome) &&
(seq_header->color_config.separate_uv_delta_q);
conditionally_set_flags(&v4l2_quant->flags, diff_uv_delta,
V4L2_AV1_QUANTIZATION_FLAG_DIFF_UV_DELTA);
conditionally_set_flags(&v4l2_quant->flags, frm_header.delta_q.present,
V4L2_AV1_QUANTIZATION_FLAG_DELTA_Q_PRESENT);
// |scale| is used to store |delta_q_res| value. This is because libgav1 uses
// the same struct |Delta| both for quantizer index delta parameters and loop
// filter delta parameters.
v4l2_quant->delta_q_res = frm_header.delta_q.scale;
}
// Section 5.9.14. Segmentation params syntax
void FillSegmentationParams(struct v4l2_av1_segmentation* v4l2_seg,
const libgav1::Segmentation& seg) {
conditionally_set_flags(&v4l2_seg->flags, seg.enabled,
V4L2_AV1_SEGMENTATION_FLAG_ENABLED);
conditionally_set_flags(&v4l2_seg->flags, seg.update_map,
V4L2_AV1_SEGMENTATION_FLAG_UPDATE_MAP);
conditionally_set_flags(&v4l2_seg->flags, seg.temporal_update,
V4L2_AV1_SEGMENTATION_FLAG_TEMPORAL_UPDATE);
conditionally_set_flags(&v4l2_seg->flags, seg.update_data,
V4L2_AV1_SEGMENTATION_FLAG_UPDATE_DATA);
conditionally_set_flags(&v4l2_seg->flags, seg.segment_id_pre_skip,
V4L2_AV1_SEGMENTATION_FLAG_SEG_ID_PRE_SKIP);
static_assert(
std::size(decltype(v4l2_seg->feature_enabled){}) == libgav1::kMaxSegments,
"Invalid size of |feature_enabled| array in |v4l2_av1_segmentation| "
"struct");
static_assert(
std::size(decltype(v4l2_seg->feature_data){}) == libgav1::kMaxSegments &&
std::extent<decltype(v4l2_seg->feature_data), 0>::value ==
libgav1::kSegmentFeatureMax,
"Invalid size of |feature_data| array in |v4l2_av1_segmentation| struct");
for (size_t i = 0; i < libgav1::kMaxSegments; ++i) {
for (size_t j = 0; j < libgav1::kSegmentFeatureMax; ++j) {
v4l2_seg->feature_enabled[i] |= (seg.feature_enabled[i][j] << j);
v4l2_seg->feature_data[i][j] = seg.feature_data[i][j];
}
}
v4l2_seg->last_active_seg_id = seg.last_active_segment_id;
}
// Section 5.9.19. CDEF params syntax
void FillCdefParams(struct v4l2_av1_cdef* v4l2_cdef,
const libgav1::Cdef& cdef,
uint8_t color_bitdepth) {
// Damping value parsed in libgav1 is from the spec + (bitdepth - 8).
// All the strength values parsed in libgav1 are from the spec and left
// shifted by (bitdepth - 8).
CHECK_GE(color_bitdepth, 8u);
const uint8_t coeff_shift = color_bitdepth - 8u;
v4l2_cdef->damping_minus_3 =
base::checked_cast<uint8_t>(cdef.damping - coeff_shift - 3u);
v4l2_cdef->bits = cdef.bits;
static_assert(std::size(decltype(v4l2_cdef->y_pri_strength){}) ==
libgav1::kMaxCdefStrengths,
"Invalid size of cdef y_pri_strength strength");
static_assert(std::size(decltype(v4l2_cdef->y_sec_strength){}) ==
libgav1::kMaxCdefStrengths,
"Invalid size of cdef y_sec_strength strength");
static_assert(std::size(decltype(v4l2_cdef->uv_pri_strength){}) ==
libgav1::kMaxCdefStrengths,
"Invalid size of cdef uv_pri_strength strength");
static_assert(std::size(decltype(v4l2_cdef->uv_sec_strength){}) ==
libgav1::kMaxCdefStrengths,
"Invalid size of cdef uv_sec_strength strength");
SafeArrayMemcpy(v4l2_cdef->y_pri_strength, cdef.y_primary_strength);
SafeArrayMemcpy(v4l2_cdef->y_sec_strength, cdef.y_secondary_strength);
SafeArrayMemcpy(v4l2_cdef->uv_pri_strength, cdef.uv_primary_strength);
SafeArrayMemcpy(v4l2_cdef->uv_sec_strength, cdef.uv_secondary_strength);
}
// 5.9.20. Loop restoration params syntax
void FillLoopRestorationParams(v4l2_av1_loop_restoration* v4l2_lr,
const libgav1::LoopRestoration& lr) {
for (size_t i = 0; i < V4L2_AV1_NUM_PLANES_MAX; i++) {
switch (lr.type[i]) {
case libgav1::LoopRestorationType::kLoopRestorationTypeNone:
v4l2_lr->frame_restoration_type[i] = V4L2_AV1_FRAME_RESTORE_NONE;
break;
case libgav1::LoopRestorationType::kLoopRestorationTypeWiener:
v4l2_lr->frame_restoration_type[i] = V4L2_AV1_FRAME_RESTORE_WIENER;
break;
case libgav1::LoopRestorationType::kLoopRestorationTypeSgrProj:
v4l2_lr->frame_restoration_type[i] = V4L2_AV1_FRAME_RESTORE_SGRPROJ;
break;
case libgav1::LoopRestorationType::kLoopRestorationTypeSwitchable:
v4l2_lr->frame_restoration_type[i] = V4L2_AV1_FRAME_RESTORE_SWITCHABLE;
break;
default:
NOTREACHED() << "Invalid loop restoration type";
}
if (v4l2_lr->frame_restoration_type[i] != V4L2_AV1_FRAME_RESTORE_NONE) {
conditionally_set_flags(&v4l2_lr->flags, true,
V4L2_AV1_LOOP_RESTORATION_FLAG_USES_LR);
conditionally_set_flags(&v4l2_lr->flags, i > 0,
V4L2_AV1_LOOP_RESTORATION_FLAG_USES_CHROMA_LR);
}
}
const bool use_loop_restoration =
std::find_if(std::begin(lr.type),
std::begin(lr.type) + libgav1::kMaxPlanes,
[](const auto type) {
return type != libgav1::kLoopRestorationTypeNone;
}) != (lr.type + libgav1::kMaxPlanes);
if (!use_loop_restoration)
return;
DCHECK_GE(lr.unit_size_log2[0], lr.unit_size_log2[1]);
DCHECK_LE(lr.unit_size_log2[0] - lr.unit_size_log2[1], 1);
v4l2_lr->lr_unit_shift = lr.unit_size_log2[0] - 6;
v4l2_lr->lr_uv_shift = lr.unit_size_log2[0] - lr.unit_size_log2[1];
constexpr uint32_t kAv1RestorationTileSizeMax = 256;
// AV1 spec (p.52) uses this formula with hard coded value 2.
v4l2_lr->loop_restoration_size[0] =
kAv1RestorationTileSizeMax >> (2 - v4l2_lr->lr_unit_shift);
v4l2_lr->loop_restoration_size[1] =
v4l2_lr->loop_restoration_size[0] >> v4l2_lr->lr_uv_shift;
v4l2_lr->loop_restoration_size[2] =
v4l2_lr->loop_restoration_size[0] >> v4l2_lr->lr_uv_shift;
}
// Section 5.9.15. Tile info syntax
void FillTileInfo(v4l2_av1_tile_info* v4l2_ti, const libgav1::TileInfo& ti) {
conditionally_set_flags(&v4l2_ti->flags, ti.uniform_spacing,
V4L2_AV1_TILE_INFO_FLAG_UNIFORM_TILE_SPACING);
static_assert(std::size(decltype(v4l2_ti->mi_col_starts){}) ==
(libgav1::kMaxTileColumns + 1),
"Size of |mi_col_starts| array in |v4l2_av1_tile_info| struct "
"does not match libgav1 expectation");
for (size_t i = 0; i < libgav1::kMaxTileColumns + 1; i++)
v4l2_ti->mi_col_starts[i] =
base::checked_cast<uint32_t>(ti.tile_column_start[i]);
static_assert(std::size(decltype(v4l2_ti->mi_row_starts){}) ==
(libgav1::kMaxTileRows + 1),
"Size of |mi_row_starts| array in |v4l2_av1_tile_info| struct "
"does not match libgav1 expectation");
for (size_t i = 0; i < libgav1::kMaxTileRows + 1; i++)
v4l2_ti->mi_row_starts[i] =
base::checked_cast<uint32_t>(ti.tile_row_start[i]);
if (!ti.uniform_spacing) {
// Confirmed that |kMaxTileColumns| is enough size for
// |width_in_sbs_minus_1| and |kMaxTileRows| is enough size for
// |height_in_sbs_minus_1|
// https://b.corp.google.com/issues/187828854#comment19
static_assert(
std::size(decltype(v4l2_ti->width_in_sbs_minus_1){}) ==
libgav1::kMaxTileColumns,
"Size of |width_in_sbs_minus_1| array in |v4l2_av1_tile_info| struct "
"does not match libgav1 expectation");
for (size_t i = 0; i < libgav1::kMaxTileColumns; i++) {
if (ti.tile_column_width_in_superblocks[i] >= 1) {
v4l2_ti->width_in_sbs_minus_1[i] = base::checked_cast<uint32_t>(
ti.tile_column_width_in_superblocks[i] - 1);
}
}
static_assert(
std::size(decltype(v4l2_ti->height_in_sbs_minus_1){}) ==
libgav1::kMaxTileRows,
"Size of |height_in_sbs_minus_1| array in |v4l2_av1_tile_info| struct "
"does not match libgav1 expectation");
for (size_t i = 0; i < libgav1::kMaxTileRows; i++) {
if (ti.tile_row_height_in_superblocks[i] >= 1) {
v4l2_ti->height_in_sbs_minus_1[i] = base::checked_cast<uint32_t>(
ti.tile_row_height_in_superblocks[i] - 1);
}
}
}
v4l2_ti->tile_size_bytes = ti.tile_size_bytes;
v4l2_ti->context_update_tile_id = ti.context_update_id;
v4l2_ti->tile_cols = ti.tile_columns;
v4l2_ti->tile_rows = ti.tile_rows;
}
// Section 5.9.24. Global motion params syntax
void FillGlobalMotionParams(
v4l2_av1_global_motion* v4l2_gm,
const std::array<libgav1::GlobalMotion, libgav1::kNumReferenceFrameTypes>&
gm_array) {
// gm_array[0] (for kReferenceFrameIntra) is not used because global motion is
// not relevant for intra frames
for (size_t i = 1; i < libgav1::kNumReferenceFrameTypes; ++i) {
auto gm = gm_array[i];
switch (gm.type) {
case libgav1::kGlobalMotionTransformationTypeIdentity:
v4l2_gm->type[i] = V4L2_AV1_WARP_MODEL_IDENTITY;
break;
case libgav1::kGlobalMotionTransformationTypeTranslation:
v4l2_gm->type[i] = V4L2_AV1_WARP_MODEL_TRANSLATION;
conditionally_set_flags(&v4l2_gm->flags[i], true,
V4L2_AV1_GLOBAL_MOTION_FLAG_IS_TRANSLATION);
break;
case libgav1::kGlobalMotionTransformationTypeRotZoom:
v4l2_gm->type[i] = V4L2_AV1_WARP_MODEL_ROTZOOM;
conditionally_set_flags(&v4l2_gm->flags[i], true,
V4L2_AV1_GLOBAL_MOTION_FLAG_IS_ROT_ZOOM);
break;
case libgav1::kGlobalMotionTransformationTypeAffine:
v4l2_gm->type[i] = V4L2_AV1_WARP_MODEL_AFFINE;
conditionally_set_flags(&v4l2_gm->flags[i], true,
V4L2_AV1_WARP_MODEL_AFFINE);
break;
default:
NOTREACHED() << "Invalid global motion transformation type, "
<< v4l2_gm->type[i];
}
conditionally_set_flags(
&v4l2_gm->flags[i],
gm.type != libgav1::kGlobalMotionTransformationTypeIdentity,
V4L2_AV1_GLOBAL_MOTION_FLAG_IS_GLOBAL);
constexpr auto kNumGlobalMotionParams = std::size(decltype(gm.params){});
for (size_t j = 0; j < kNumGlobalMotionParams; ++j) {
static_assert(
std::is_same<decltype(v4l2_gm->params[0][0]), int32_t&>::value,
"|v4l2_av1_global_motion::params|'s data type must be int32_t "
"starting from AV1 uAPI v4");
v4l2_gm->params[i][j] = gm.params[j];
}
conditionally_set_flags(&v4l2_gm->invalid, !libgav1::SetupShear(&gm),
V4L2_AV1_GLOBAL_MOTION_IS_INVALID(i));
}
}
// Section 5.11. Tile Group OBU syntax
void FillTileGroupParams(
std::vector<struct v4l2_ctrl_av1_tile_group_entry>*
tile_group_entry_vectors,
const base::span<const uint8_t> frame_obu_data,
const libgav1::TileInfo& tile_info,
const libgav1::Vector<libgav1::TileBuffer>& tile_buffers) {
// TODO(stevecho): This could happen in rare cases (for example, if there is a
// Metadata OBU after the TileGroup OBU). We currently do not have a reason to
// handle those cases. This is also the case in libgav1 at the moment.
CHECK(!tile_buffers.empty());
const size_t tile_columns = tile_info.tile_columns;
CHECK_GT(tile_columns, 0u);
const uint16_t num_tiles = base::checked_cast<uint16_t>(tile_buffers.size());
for (uint16_t tile_index = 0; tile_index < num_tiles; ++tile_index) {
struct v4l2_ctrl_av1_tile_group_entry tile_group_entry_params = {};
CHECK(tile_buffers[tile_index].data >= frame_obu_data.data());
tile_group_entry_params.tile_offset = base::checked_cast<uint32_t>(
tile_buffers[tile_index].data - frame_obu_data.data());
tile_group_entry_params.tile_size = tile_buffers[tile_index].size;
// The tiles are row-major. We use the number of columns |tile_columns|
// to compute computation of the row and column for a given tile.
tile_group_entry_params.tile_row =
tile_index / base::checked_cast<uint16_t>(tile_columns);
tile_group_entry_params.tile_col =
tile_index % base::checked_cast<uint16_t>(tile_columns);
tile_group_entry_vectors->push_back(tile_group_entry_params);
}
}
} // namespace
Av1Decoder::Av1Decoder(std::unique_ptr<IvfParser> ivf_parser,
std::unique_ptr<V4L2IoctlShim> v4l2_ioctl,
gfx::Size display_resolution)
: VideoDecoder::VideoDecoder(std::move(v4l2_ioctl), display_resolution),
ivf_parser_(std::move(ivf_parser)),
buffer_pool_(std::make_unique<libgav1::BufferPool>(
/*on_frame_buffer_size_changed=*/nullptr,
/*get_frame_buffer=*/nullptr,
/*release_frame_buffer=*/nullptr,
/*callback_private_data=*/nullptr)),
state_(std::make_unique<libgav1::DecoderState>()) {}
Av1Decoder::~Av1Decoder() {
// We destroy the state explicitly to ensure it's destroyed before the
// |buffer_pool_|. The |buffer_pool_| checks that all the allocated frames
// are released in its destructor.
state_.reset();
DCHECK(buffer_pool_);
}
// static
std::unique_ptr<Av1Decoder> Av1Decoder::Create(
const base::MemoryMappedFile& stream) {
VLOG(2) << "Attempting to create decoder with codec "
<< media::FourccToString(kDriverCodecFourcc);
// Set up video parser.
auto ivf_parser = std::make_unique<media::IvfParser>();
media::IvfFileHeader file_header{};
if (!ivf_parser->Initialize(stream.data(), stream.length(), &file_header)) {
LOG(ERROR) << "Couldn't initialize IVF parser";
return nullptr;
}
const auto driver_codec_fourcc =
media::v4l2_test::FileFourccToDriverFourcc(file_header.fourcc);
if (driver_codec_fourcc != kDriverCodecFourcc) {
VLOG(2) << "File fourcc (" << media::FourccToString(driver_codec_fourcc)
<< ") does not match expected fourcc("
<< media::FourccToString(kDriverCodecFourcc) << ").";
return nullptr;
}
auto v4l2_ioctl = std::make_unique<V4L2IoctlShim>(kDriverCodecFourcc);
if (!v4l2_ioctl->VerifyCapabilities(kDriverCodecFourcc)) {
LOG(ERROR) << "Device doesn't support "
<< media::FourccToString(kDriverCodecFourcc) << ".";
return nullptr;
}
const gfx::Size bitstream_coded_size = GetResolutionFromBitstream(stream);
return base::WrapUnique(new Av1Decoder(
std::move(ivf_parser), std::move(v4l2_ioctl), bitstream_coded_size));
}
Av1Decoder::ParsingResult Av1Decoder::ReadNextFrame(
libgav1::RefCountedBufferPtr& current_frame) {
if (!obu_parser_ || !obu_parser_->HasData()) {
if (!ivf_parser_->ParseNextFrame(&ivf_frame_header_, &ivf_frame_data_))
return ParsingResult::kEOStream;
// The ObuParser has run out of data or did not exist in the first place. It
// has no "replace the current buffer with a new buffer of a different size"
// method; we must make a new parser.
// (std::nothrow) is required for the base class Allocable of
// libgav1::ObuParser
obu_parser_ = base::WrapUnique(new (std::nothrow) libgav1::ObuParser(
ivf_frame_data_, ivf_frame_header_.frame_size, /*operating_point=*/0,
buffer_pool_.get(), state_.get()));
if (current_sequence_header_)
obu_parser_->set_sequence_header(*current_sequence_header_);
}
const libgav1::StatusCode code = obu_parser_->ParseOneFrame(&current_frame);
if (code != libgav1::kStatusOk) {
LOG(ERROR) << "Error parsing OBU stream: " << libgav1::GetErrorString(code);
return ParsingResult::kFailed;
}
return ParsingResult::kOk;
}
void Av1Decoder::CopyFrameData(const libgav1::ObuFrameHeader& frame_hdr,
std::unique_ptr<V4L2Queue>& queue) {
CHECK_EQ(queue->num_buffers(), 1u)
<< "Only 1 buffer is expected to be used for OUTPUT queue for now.";
CHECK_EQ(queue->num_planes(), 1u)
<< "Number of planes is expected to be 1 for OUTPUT queue.";
scoped_refptr<MmappedBuffer> buffer = queue->GetBuffer(0);
buffer->mmapped_planes()[0].CopyIn(ivf_frame_data_,
ivf_frame_header_.frame_size);
}
// 5.9.2. Uncompressed header syntax
void Av1Decoder::SetupFrameParams(
struct v4l2_ctrl_av1_frame* v4l2_frame_params,
const absl::optional<libgav1::ObuSequenceHeader>& seq_header,
const libgav1::ObuFrameHeader& frm_header) {
FillLoopFilterParams(&v4l2_frame_params->loop_filter, frm_header.loop_filter);
FillLoopFilterDeltaParams(&v4l2_frame_params->loop_filter,
frm_header.delta_lf);
FillQuantizationParams(&v4l2_frame_params->quantization,
frm_header.quantizer);
FillQuantizerIndexDeltaParams(&v4l2_frame_params->quantization, seq_header,
frm_header);
FillSegmentationParams(&v4l2_frame_params->segmentation,
frm_header.segmentation);
const auto color_bitdepth = seq_header->color_config.bitdepth;
FillCdefParams(&v4l2_frame_params->cdef, frm_header.cdef,
base::strict_cast<int8_t>(color_bitdepth));
FillLoopRestorationParams(&v4l2_frame_params->loop_restoration,
frm_header.loop_restoration);
FillTileInfo(&v4l2_frame_params->tile_info, frm_header.tile_info);
FillGlobalMotionParams(&v4l2_frame_params->global_motion,
frm_header.global_motion);
conditionally_set_u32_flags(&v4l2_frame_params->flags, frm_header.show_frame,
V4L2_AV1_FRAME_FLAG_SHOW_FRAME);
conditionally_set_u32_flags(&v4l2_frame_params->flags,
frm_header.showable_frame,
V4L2_AV1_FRAME_FLAG_SHOWABLE_FRAME);
conditionally_set_u32_flags(&v4l2_frame_params->flags,
frm_header.error_resilient_mode,
V4L2_AV1_FRAME_FLAG_ERROR_RESILIENT_MODE);
// libgav1 header has |enable_cdf_update| instead of |disable_cdf_update|.
conditionally_set_u32_flags(&v4l2_frame_params->flags,
!frm_header.enable_cdf_update,
V4L2_AV1_FRAME_FLAG_DISABLE_CDF_UPDATE);
conditionally_set_u32_flags(&v4l2_frame_params->flags,
frm_header.allow_screen_content_tools,
V4L2_AV1_FRAME_FLAG_ALLOW_SCREEN_CONTENT_TOOLS);
conditionally_set_u32_flags(&v4l2_frame_params->flags,
frm_header.force_integer_mv,
V4L2_AV1_FRAME_FLAG_FORCE_INTEGER_MV);
conditionally_set_u32_flags(&v4l2_frame_params->flags,
frm_header.allow_intrabc,
V4L2_AV1_FRAME_FLAG_ALLOW_INTRABC);
conditionally_set_u32_flags(&v4l2_frame_params->flags,
frm_header.use_superres,
V4L2_AV1_FRAME_FLAG_USE_SUPERRES);
conditionally_set_u32_flags(&v4l2_frame_params->flags,
frm_header.allow_high_precision_mv,
V4L2_AV1_FRAME_FLAG_ALLOW_HIGH_PRECISION_MV);
conditionally_set_u32_flags(&v4l2_frame_params->flags,
frm_header.is_motion_mode_switchable,
V4L2_AV1_FRAME_FLAG_IS_MOTION_MODE_SWITCHABLE);
conditionally_set_u32_flags(&v4l2_frame_params->flags,
frm_header.use_ref_frame_mvs,
V4L2_AV1_FRAME_FLAG_USE_REF_FRAME_MVS);
// libgav1 header has |enable_frame_end_update_cdf| instead.
conditionally_set_u32_flags(&v4l2_frame_params->flags,
!frm_header.enable_frame_end_update_cdf,
V4L2_AV1_FRAME_FLAG_DISABLE_FRAME_END_UPDATE_CDF);
conditionally_set_u32_flags(&v4l2_frame_params->flags,
frm_header.tile_info.uniform_spacing,
V4L2_AV1_FRAME_FLAG_UNIFORM_TILE_SPACING);
conditionally_set_u32_flags(&v4l2_frame_params->flags,
frm_header.allow_warped_motion,
V4L2_AV1_FRAME_FLAG_ALLOW_WARPED_MOTION);
conditionally_set_u32_flags(&v4l2_frame_params->flags,
frm_header.reference_mode_select,
V4L2_AV1_FRAME_FLAG_REFERENCE_SELECT);
conditionally_set_u32_flags(&v4l2_frame_params->flags,
frm_header.reduced_tx_set,
V4L2_AV1_FRAME_FLAG_REDUCED_TX_SET);
conditionally_set_u32_flags(&v4l2_frame_params->flags,
frm_header.skip_mode_frame[0] > 0,
V4L2_AV1_FRAME_FLAG_SKIP_MODE_ALLOWED);
conditionally_set_u32_flags(&v4l2_frame_params->flags,
frm_header.skip_mode_present,
V4L2_AV1_FRAME_FLAG_SKIP_MODE_PRESENT);
conditionally_set_u32_flags(&v4l2_frame_params->flags,
frm_header.frame_size_override_flag,
V4L2_AV1_FRAME_FLAG_FRAME_SIZE_OVERRIDE);
// libgav1 header doesn't have |buffer_removal_time_present_flag|.
conditionally_set_u32_flags(&v4l2_frame_params->flags,
frm_header.buffer_removal_time[0] > 0,
V4L2_AV1_FRAME_FLAG_BUFFER_REMOVAL_TIME_PRESENT);
conditionally_set_u32_flags(&v4l2_frame_params->flags,
frm_header.frame_refs_short_signaling,
V4L2_AV1_FRAME_FLAG_FRAME_REFS_SHORT_SIGNALING);
switch (frm_header.frame_type) {
case libgav1::kFrameKey:
v4l2_frame_params->frame_type = V4L2_AV1_KEY_FRAME;
break;
case libgav1::kFrameInter:
v4l2_frame_params->frame_type = V4L2_AV1_INTER_FRAME;
break;
case libgav1::kFrameIntraOnly:
v4l2_frame_params->frame_type = V4L2_AV1_INTRA_ONLY_FRAME;
break;
case libgav1::kFrameSwitch:
v4l2_frame_params->frame_type = V4L2_AV1_SWITCH_FRAME;
break;
default:
NOTREACHED() << "Invalid frame type, " << frm_header.frame_type;
}
v4l2_frame_params->order_hint = frm_header.order_hint;
v4l2_frame_params->superres_denom = frm_header.superres_scale_denominator;
v4l2_frame_params->upscaled_width = frm_header.upscaled_width;
switch (frm_header.interpolation_filter) {
case libgav1::kInterpolationFilterEightTap:
v4l2_frame_params->interpolation_filter =
V4L2_AV1_INTERPOLATION_FILTER_EIGHTTAP;
break;
case libgav1::kInterpolationFilterEightTapSmooth:
v4l2_frame_params->interpolation_filter =
V4L2_AV1_INTERPOLATION_FILTER_EIGHTTAP_SMOOTH;
break;
case libgav1::kInterpolationFilterEightTapSharp:
v4l2_frame_params->interpolation_filter =
V4L2_AV1_INTERPOLATION_FILTER_EIGHTTAP_SHARP;
break;
case libgav1::kInterpolationFilterBilinear:
v4l2_frame_params->interpolation_filter =
V4L2_AV1_INTERPOLATION_FILTER_BILINEAR;
break;
case libgav1::kInterpolationFilterSwitchable:
v4l2_frame_params->interpolation_filter =
V4L2_AV1_INTERPOLATION_FILTER_SWITCHABLE;
break;
default:
NOTREACHED() << "Invalid interpolation filter, "
<< frm_header.interpolation_filter;
}
switch (frm_header.tx_mode) {
case libgav1::kTxModeOnly4x4:
v4l2_frame_params->tx_mode = V4L2_AV1_TX_MODE_ONLY_4X4;
break;
case libgav1::kTxModeLargest:
v4l2_frame_params->tx_mode = V4L2_AV1_TX_MODE_LARGEST;
break;
case libgav1::kTxModeSelect:
v4l2_frame_params->tx_mode = V4L2_AV1_TX_MODE_SELECT;
break;
default:
NOTREACHED() << "Invalid tx mode, " << frm_header.tx_mode;
}
v4l2_frame_params->frame_width_minus_1 = frm_header.width - 1;
v4l2_frame_params->frame_height_minus_1 = frm_header.height - 1;
v4l2_frame_params->render_width_minus_1 = frm_header.render_width - 1;
v4l2_frame_params->render_height_minus_1 = frm_header.render_height - 1;
v4l2_frame_params->current_frame_id = frm_header.current_frame_id;
v4l2_frame_params->primary_ref_frame = frm_header.primary_reference_frame;
SafeArrayMemcpy(v4l2_frame_params->buffer_removal_time,
frm_header.buffer_removal_time);
v4l2_frame_params->refresh_frame_flags = frm_header.refresh_frame_flags;
if (frm_header.frame_type == libgav1::kFrameKey && frm_header.show_frame) {
for (size_t i = 0; i < libgav1::kNumReferenceFrameTypes; i++)
ref_order_hint_[i] = 0;
}
// The first slot in |order_hints| is reserved for intra frame, so it is not
// used and will always be 0.
static_assert(std::size(decltype(v4l2_frame_params->order_hints){}) ==
libgav1::kNumInterReferenceFrameTypes + 1,
"Invalid size of |order_hints| array");
if (!libgav1::IsIntraFrame(frm_header.frame_type)) {
for (size_t i = 0; i < libgav1::kNumInterReferenceFrameTypes; i++) {
v4l2_frame_params->order_hints[i + 1] =
ref_order_hint_[frm_header.reference_frame_index[i]];
}
}
// TODO(b/230891887): use uint64_t when v4l2_timeval_to_ns() function is used.
constexpr uint32_t kInvalidSurface = std::numeric_limits<uint32_t>::max();
for (size_t i = 0; i < libgav1::kNumReferenceFrameTypes; ++i) {
constexpr size_t kTimestampToNanoSecs = 1000;
// |reference_frame_ts| is needed to use previously decoded frames
// from reference frames list.
const auto reference_frame_ts =
ref_frames_[i] ? ref_frames_[i]->frame_number() * kTimestampToNanoSecs
: kInvalidSurface;
v4l2_frame_params->reference_frame_ts[i] = reference_frame_ts;
}
static_assert(std::size(decltype(v4l2_frame_params->ref_frame_idx){}) ==
libgav1::kNumInterReferenceFrameTypes,
"Invalid size of |ref_frame_idx| array");
for (size_t i = 0; i < libgav1::kNumInterReferenceFrameTypes; i++) {
static_assert(std::is_same<decltype(v4l2_frame_params->ref_frame_idx[0]),
int8_t&>::value,
"|v4l2_ctrl_av1_frame::ref_frame_idx|'s data type must be "
"int8_t starting from AV1 uAPI v4");
v4l2_frame_params->ref_frame_idx[i] = frm_header.reference_frame_index[i];
}
v4l2_frame_params->skip_mode_frame[0] =
base::checked_cast<__u8>(frm_header.skip_mode_frame[0]);
v4l2_frame_params->skip_mode_frame[1] =
base::checked_cast<__u8>(frm_header.skip_mode_frame[1]);
}
std::set<int> Av1Decoder::RefreshReferenceSlots(
const libgav1::ObuFrameHeader& frame_hdr,
const libgav1::RefCountedBufferPtr current_frame,
const scoped_refptr<MmappedBuffer> buffer,
const uint32_t last_queued_buffer_id) {
state_->UpdateReferenceFrames(
current_frame, base::strict_cast<int>(frame_hdr.refresh_frame_flags));
static_assert(
kAv1NumRefFrames == sizeof(frame_hdr.refresh_frame_flags) * CHAR_BIT,
"|refresh_frame_flags| size must be equal to |kAv1NumRefFrames|");
const std::bitset<kAv1NumRefFrames> refresh_frame_slots(
frame_hdr.refresh_frame_flags);
std::set<int> reusable_buffer_ids;
constexpr uint8_t kRefreshFrameFlagsAll = 0xFF;
// If |show_existing_frame| = 1 and the frame to show is a key frame, the
// reference frame loading process as specified in section 7.21 of the AV1
// spec is invoked.
const bool is_show_existing_key_frame =
(frame_hdr.show_existing_frame &&
(state_->reference_frame[frame_hdr.frame_to_show]->frame_type() ==
libgav1::kFrameKey));
if (frame_hdr.refresh_frame_flags == kRefreshFrameFlagsAll ||
is_show_existing_key_frame) {
// After decoding a key frame, all CAPTURE buffers can be reused except the
// CAPTURE buffer corresponding to the key frame.
for (size_t i = 0; i < kNumberOfBuffersInCaptureQueue; i++)
reusable_buffer_ids.insert(i);
reusable_buffer_ids.erase(buffer->buffer_id());
// Note that the CAPTURE buffer for previous frame can be used as well,
// but it is already queued again at this point.
reusable_buffer_ids.erase(last_queued_buffer_id);
// Updates to assign current key frame as a reference frame for all
// reference frame slots in the reference frames list.
ref_frames_.fill(buffer);
if (is_show_existing_key_frame) {
for (size_t i = 0; i < libgav1::kNumReferenceFrameTypes; i++)
ref_order_hint_[i] = ref_order_hint_[frame_hdr.frame_to_show];
}
return reusable_buffer_ids;
}
constexpr uint8_t kRefreshFrameFlagsNone = 0;
if (frame_hdr.refresh_frame_flags == kRefreshFrameFlagsNone) {
// Indicates to reuse currently decoded CAPTURE buffer.
reusable_buffer_ids.insert(buffer->buffer_id());
return reusable_buffer_ids;
}
// More than one slot in |refresh_frame_flags| can be set.
for (size_t i = 0; i < kAv1NumRefFrames; i++) {
if (!refresh_frame_slots[i])
continue;
// It is not required to check whether existing reference frame slot is
// already pointing to a reference frame. This is because reference
// frame slots are empty only after the first key frame decoding.
const uint16_t reusable_candidate_buffer_id = ref_frames_[i]->buffer_id();
reusable_buffer_ids.insert(reusable_candidate_buffer_id);
// Checks to make sure |reusable_candidate_buffer_id| is not used in
// different reference frame slots in the reference frames list. If
// |reusable_candidate_buffer_id| is already being used, then it is no
// longer qualified as a reusable buffer. Thus, it is removed from
// |reusable_buffer_ids|.
for (size_t j = 0; j < kAv1NumRefFrames; j++) {
const bool is_refresh_slot_not_used = (refresh_frame_slots[j] == false);
const bool is_candidate_used =
(ref_frames_[j]->buffer_id() == reusable_candidate_buffer_id);
if (is_refresh_slot_not_used && is_candidate_used) {
reusable_buffer_ids.erase(reusable_candidate_buffer_id);
break;
}
}
ref_frames_[i] = buffer;
ref_order_hint_[i] = frame_hdr.order_hint;
}
return reusable_buffer_ids;
}
void Av1Decoder::QueueReusableBuffersInCaptureQueue(
const std::set<int> reusable_buffer_ids,
const bool is_inter_frame) {
for (const auto reusable_buffer_id : reusable_buffer_ids) {
if (!v4l2_ioctl_->QBuf(CAPTURE_queue_, reusable_buffer_id))
LOG(ERROR) << "VIDIOC_QBUF failed for CAPTURE queue.";
if (is_inter_frame)
CAPTURE_queue_->set_last_queued_buffer_id(reusable_buffer_id);
}
}
VideoDecoder::Result Av1Decoder::DecodeNextFrame(std::vector<uint8_t>& y_plane,
std::vector<uint8_t>& u_plane,
std::vector<uint8_t>& v_plane,
gfx::Size& size,
const int frame_number) {
libgav1::RefCountedBufferPtr current_frame;
const ParsingResult parser_res = ReadNextFrame(current_frame);
if (parser_res != ParsingResult::kOk) {
LOG_ASSERT(parser_res == ParsingResult::kEOStream)
<< "Failed to parse next frame.";
return VideoDecoder::kEOStream;
}
const bool is_OUTPUT_queue_new = !OUTPUT_queue_;
if (!OUTPUT_queue_) {
CreateOUTPUTQueue(kDriverCodecFourcc);
}
libgav1::ObuFrameHeader current_frame_header = obu_parser_->frame_header();
if (obu_parser_->sequence_header_changed())
current_sequence_header_.emplace(obu_parser_->sequence_header());
LOG_ASSERT(current_sequence_header_)
<< "Sequence header missing for decoding.";
if (current_frame_header.show_existing_frame) {
last_decoded_frame_visible_ = true;
} else {
last_decoded_frame_visible_ = current_frame_header.show_frame;
}
VLOG_IF(2, !last_decoded_frame_visible_) << "not displayed frame";
for (size_t i = 0; i < kAv1NumRefFrames; ++i) {
if (state_->reference_frame[i] != nullptr && ref_frames_[i] == nullptr) {
LOG_ASSERT(false) << "The state of the reference frames are different "
"between |ref_frames_| and |state_|";
}
if (state_->reference_frame[i] == nullptr && ref_frames_[i] != nullptr)
ref_frames_[i].reset();
}
if (current_frame_header.show_existing_frame) {
scoped_refptr<MmappedBuffer> repeated_frame_buffer =
ref_frames_[current_frame_header.frame_to_show];
ConvertToYUV(y_plane, u_plane, v_plane, OUTPUT_queue_->resolution(),
repeated_frame_buffer->mmapped_planes(),
CAPTURE_queue_->resolution(), CAPTURE_queue_->fourcc());
// Repeated frames normally don't need to update reference frames. But in
// this special case when the repeated frame is pointing to a key frame, all
// the reference frames have to be updated to the key frame pointed by the
// repeated frame.
if (state_->reference_frame[current_frame_header.frame_to_show]
->frame_type() == libgav1::kFrameKey) {
const std::set<int> reusable_buffer_ids =
RefreshReferenceSlots(current_frame_header, current_frame,
ref_frames_[current_frame_header.frame_to_show],
CAPTURE_queue_->last_queued_buffer_id());
QueueReusableBuffersInCaptureQueue(
reusable_buffer_ids,
!libgav1::IsIntraFrame(current_frame_header.frame_type));
}
return VideoDecoder::kOk;
}
CopyFrameData(current_frame_header, OUTPUT_queue_);
LOG_ASSERT(OUTPUT_queue_->num_buffers() == 1)
<< "Too many buffers in OUTPUT queue. It is currently designed to "
"support only 1 request at a time.";
OUTPUT_queue_->GetBuffer(0)->set_frame_number(frame_number);
if (!v4l2_ioctl_->QBuf(OUTPUT_queue_, 0))
LOG(FATAL) << "VIDIOC_QBUF failed for OUTPUT queue.";
std::vector<struct v4l2_ext_control> ext_ctrl_vectors;
struct v4l2_ctrl_av1_sequence v4l2_seq_params = {};
FillSequenceParams(&v4l2_seq_params, current_sequence_header_);
ext_ctrl_vectors.push_back({.id = V4L2_CID_STATELESS_AV1_SEQUENCE,
.size = sizeof(v4l2_seq_params),
.ptr = &v4l2_seq_params});
struct v4l2_ctrl_av1_frame v4l2_frame_params = {};
SetupFrameParams(&v4l2_frame_params, current_sequence_header_,
current_frame_header);
ext_ctrl_vectors.push_back({.id = V4L2_CID_STATELESS_AV1_FRAME,
.size = sizeof(v4l2_frame_params),
.ptr = &v4l2_frame_params});
std::vector<struct v4l2_ctrl_av1_tile_group_entry> tile_group_entry_vectors;
FillTileGroupParams(
&tile_group_entry_vectors,
base::make_span(ivf_frame_data_, ivf_frame_header_.frame_size),
current_frame_header.tile_info, obu_parser_->tile_buffers());
ext_ctrl_vectors.push_back({.id = V4L2_CID_STATELESS_AV1_TILE_GROUP_ENTRY,
.size = base::checked_cast<__u32>(
tile_group_entry_vectors.size() *
sizeof(v4l2_ctrl_av1_tile_group_entry)),
.ptr = &tile_group_entry_vectors[0]});
struct v4l2_ext_controls ext_ctrls = {.count = base::checked_cast<__u32>(ext_ctrl_vectors.size()),
.controls = &ext_ctrl_vectors[0]};
// Before the CAPTURE queue is set up the first frame must be parsed by the
// driver. This is done so that when VIDIOC_G_FMT is called the frame
// dimensions and format will be ready. Specifying V4L2_CTRL_WHICH_CUR_VAL
// when VIDIOC_S_EXT_CTRLS processes the request immediately so that the frame
// is parsed by the driver and the state is readied.
v4l2_ioctl_->SetExtCtrls(OUTPUT_queue_, &ext_ctrls, is_OUTPUT_queue_new);
v4l2_ioctl_->MediaRequestIocQueue(OUTPUT_queue_);
if (!CAPTURE_queue_) {
CreateCAPTUREQueue(kNumberOfBuffersInCaptureQueue);
}
uint32_t buffer_id;
v4l2_ioctl_->DQBuf(CAPTURE_queue_, &buffer_id);
scoped_refptr<MmappedBuffer> buffer = CAPTURE_queue_->GetBuffer(buffer_id);
ConvertToYUV(y_plane, u_plane, v_plane, OUTPUT_queue_->resolution(),
buffer->mmapped_planes(), CAPTURE_queue_->resolution(),
CAPTURE_queue_->fourcc());
const std::set<int> reusable_buffer_ids = RefreshReferenceSlots(
current_frame_header, current_frame, CAPTURE_queue_->GetBuffer(buffer_id),
CAPTURE_queue_->last_queued_buffer_id());
QueueReusableBuffersInCaptureQueue(
reusable_buffer_ids,
!libgav1::IsIntraFrame(current_frame_header.frame_type));
v4l2_ioctl_->DQBuf(OUTPUT_queue_, &buffer_id);
v4l2_ioctl_->MediaRequestIocReinit(OUTPUT_queue_);
return VideoDecoder::kOk;
}
} // namespace v4l2_test
} // namespace media