| // 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(¤t_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 |