|  | /* | 
|  | * Copyright (c) 2022, Alliance for Open Media. All rights reserved | 
|  | * | 
|  | * This source code is subject to the terms of the BSD 2 Clause License and | 
|  | * the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License | 
|  | * was not distributed with this source code in the LICENSE file, you can | 
|  | * obtain it at www.aomedia.org/license/software. If the Alliance for Open | 
|  | * Media Patent License 1.0 was not distributed with this source code in the | 
|  | * PATENTS file, you can obtain it at www.aomedia.org/license/patent. | 
|  | */ | 
|  |  | 
|  | #include <algorithm> | 
|  | #include <set> | 
|  | #include <utility> | 
|  | #include <tuple> | 
|  | #include <vector> | 
|  |  | 
|  | #include "av1/qmode_rc/reference_manager.h" | 
|  | #include "av1/qmode_rc/ratectrl_qmode.h" | 
|  |  | 
|  | namespace aom { | 
|  |  | 
|  | void RefFrameManager::Reset() { | 
|  | free_ref_idx_list_.clear(); | 
|  | for (int i = 0; i < static_cast<int>(ref_frame_table_.size()); ++i) { | 
|  | free_ref_idx_list_.push_back(i); | 
|  | ref_frame_table_[i] = GopFrameInvalid(); | 
|  | } | 
|  | forward_stack_.clear(); | 
|  | backward_queue_.clear(); | 
|  | last_queue_.clear(); | 
|  | } | 
|  |  | 
|  | int RefFrameManager::AllocateRefIdx() { | 
|  | if (free_ref_idx_list_.empty()) { | 
|  | size_t backward_size = backward_queue_.size(); | 
|  | size_t last_size = last_queue_.size(); | 
|  | if (last_size >= backward_size) { | 
|  | int ref_idx = last_queue_.front(); | 
|  | last_queue_.pop_front(); | 
|  | free_ref_idx_list_.push_back(ref_idx); | 
|  | } else { | 
|  | int ref_idx = backward_queue_.front(); | 
|  | backward_queue_.pop_front(); | 
|  | free_ref_idx_list_.push_back(ref_idx); | 
|  | } | 
|  | } | 
|  |  | 
|  | int ref_idx = free_ref_idx_list_.front(); | 
|  | free_ref_idx_list_.pop_front(); | 
|  | return ref_idx; | 
|  | } | 
|  |  | 
|  | int RefFrameManager::GetRefFrameCountByType( | 
|  | RefUpdateType ref_update_type) const { | 
|  | size_t cnt = 0; | 
|  | switch (ref_update_type) { | 
|  | case RefUpdateType::kForward: cnt = forward_stack_.size(); break; | 
|  | case RefUpdateType::kBackward: cnt = backward_queue_.size(); break; | 
|  | case RefUpdateType::kLast: cnt = last_queue_.size(); break; | 
|  | case RefUpdateType::kNone: cnt = 0; break; | 
|  | } | 
|  | return static_cast<int>(cnt); | 
|  | } | 
|  |  | 
|  | int RefFrameManager::GetRefFrameCount() const { | 
|  | return GetRefFrameCountByType(RefUpdateType::kForward) + | 
|  | GetRefFrameCountByType(RefUpdateType::kBackward) + | 
|  | GetRefFrameCountByType(RefUpdateType::kLast); | 
|  | } | 
|  |  | 
|  | // TODO(angiebird): Add unit test. | 
|  | // Find the ref_idx corresponding to a ref_update_type. | 
|  | // Return -1 if no ref frame is found. | 
|  | // The priority_idx indicate closeness between the current frame and | 
|  | // the ref frame in display order. | 
|  | // For example, ref_update_type == kForward and priority_idx == 0 means | 
|  | // find the closest ref frame in forward_stack_. | 
|  | int RefFrameManager::GetRefFrameIdxByPriority(RefUpdateType ref_update_type, | 
|  | int priority_idx) const { | 
|  | if (ref_update_type == RefUpdateType::kForward) { | 
|  | int size = static_cast<int>(forward_stack_.size()); | 
|  | // When two or more forward reference frames can be used, first get | 
|  | // the highest quality one as the ARF, then going from nearest to | 
|  | // the more distant ones in the forward reference frame list. | 
|  | if (priority_idx < size) { | 
|  | if (allow_two_fwd_frames_) { | 
|  | if (priority_idx == 0) return forward_stack_[0]; | 
|  | return forward_stack_[size - priority_idx]; | 
|  | } | 
|  |  | 
|  | // Handle the special case where only one forward reference frame | 
|  | // can be used. In this setting, we prefer the nearest frame. | 
|  | return forward_stack_[size - 1 - priority_idx]; | 
|  | } | 
|  | } else if (ref_update_type == RefUpdateType::kBackward) { | 
|  | int size = static_cast<int>(backward_queue_.size()); | 
|  | if (priority_idx < size) { | 
|  | return backward_queue_[size - priority_idx - 1]; | 
|  | } | 
|  | } else if (ref_update_type == RefUpdateType::kLast) { | 
|  | int size = static_cast<int>(last_queue_.size()); | 
|  | if (priority_idx < size) { | 
|  | return last_queue_[size - priority_idx - 1]; | 
|  | } | 
|  | } | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | // The priority_idx indicate closeness between the current frame and | 
|  | // the ref frame in display order. | 
|  | // For example, ref_update_type == kForward and priority_idx == 0 means | 
|  | // find the closest ref frame in forward_stack_. | 
|  | GopFrame RefFrameManager::GetRefFrameByPriority(RefUpdateType ref_update_type, | 
|  | int priority_idx) const { | 
|  | int ref_idx = GetRefFrameIdxByPriority(ref_update_type, priority_idx); | 
|  | if (ref_idx == -1) { | 
|  | return GopFrameInvalid(); | 
|  | } | 
|  | assert(ref_frame_table_[ref_idx].update_ref_idx == ref_idx); | 
|  | return ref_frame_table_[ref_idx]; | 
|  | } | 
|  |  | 
|  | GopFrame RefFrameManager::GetRefFrameByIndex(int ref_idx) const { | 
|  | return ref_frame_table_[ref_idx]; | 
|  | } | 
|  |  | 
|  | ReferenceName get_ref_name(RefUpdateType ref_update_type, int priority_idx, | 
|  | const std::set<ReferenceName> &used_name_set) { | 
|  | // TODO(angiebird): Find the better way to assign name lists. | 
|  | // Maybe sort the names based on how frequent each name is being used in the | 
|  | // past? | 
|  | const std::vector<ReferenceName> forward_name_list{ | 
|  | ReferenceName::kAltrefFrame,  ReferenceName::kBwdrefFrame, | 
|  | ReferenceName::kAltref2Frame, ReferenceName::kGoldenFrame, | 
|  | ReferenceName::kLast3Frame,   ReferenceName::kLast2Frame, | 
|  | ReferenceName::kLastFrame | 
|  | }; | 
|  | const std::vector<ReferenceName> backward_name_list{ | 
|  | ReferenceName::kGoldenFrame, ReferenceName::kLastFrame, | 
|  | ReferenceName::kLast2Frame,  ReferenceName::kLast3Frame, | 
|  | ReferenceName::kBwdrefFrame, ReferenceName::kAltref2Frame, | 
|  | ReferenceName::kAltrefFrame | 
|  | }; | 
|  | const std::vector<ReferenceName> last_name_list{ | 
|  | ReferenceName::kLastFrame,   ReferenceName::kLast2Frame, | 
|  | ReferenceName::kLast3Frame,  ReferenceName::kGoldenFrame, | 
|  | ReferenceName::kBwdrefFrame, ReferenceName::kAltref2Frame, | 
|  | ReferenceName::kAltrefFrame | 
|  | }; | 
|  |  | 
|  | const std::vector<ReferenceName> *name_list = nullptr; | 
|  | switch (ref_update_type) { | 
|  | case RefUpdateType::kForward: name_list = &forward_name_list; break; | 
|  | case RefUpdateType::kBackward: name_list = &backward_name_list; break; | 
|  | case RefUpdateType::kLast: name_list = &last_name_list; break; | 
|  | case RefUpdateType::kNone: break; | 
|  | } | 
|  |  | 
|  | if (name_list) { | 
|  | const int name_list_size = static_cast<int>(name_list->size()); | 
|  | for (int idx = priority_idx; idx < name_list_size; ++idx) { | 
|  | ReferenceName ref_name = name_list->at(idx); | 
|  | bool not_used = used_name_set.find(ref_name) == used_name_set.end(); | 
|  | if (not_used) return ref_name; | 
|  | } | 
|  | } | 
|  | return ReferenceName::kNoneFrame; | 
|  | } | 
|  |  | 
|  | // Generate a list of available reference frames in priority order for the | 
|  | // current to-be-coded frame. The list size should be less or equal to the size | 
|  | // of ref_frame_table_. The reference frames with smaller indices are more | 
|  | // likely to be a good reference frame. Therefore, they should be prioritized | 
|  | // when the reference frame count is limited. For example, if we plan to use 3 | 
|  | // reference frames, we should choose ref_frame_list[0], ref_frame_list[1] and | 
|  | // ref_frame_list[2]. | 
|  | std::vector<ReferenceFrame> RefFrameManager::GetRefFrameListByPriority() const { | 
|  | constexpr int round_robin_size = 3; | 
|  | const std::vector<RefUpdateType> round_robin_list{ RefUpdateType::kForward, | 
|  | RefUpdateType::kBackward, | 
|  | RefUpdateType::kLast }; | 
|  | std::vector<int> priority_idx_list(round_robin_size, 0); | 
|  | int available_ref_frames = GetRefFrameCount(); | 
|  | std::vector<ReferenceFrame> ref_frame_list; | 
|  | int ref_frame_count = 0; | 
|  | int round_robin_idx = 0; | 
|  |  | 
|  | std::set<ReferenceName> used_name_set; | 
|  | while (ref_frame_count < available_ref_frames && | 
|  | ref_frame_count < max_ref_frames_) { | 
|  | const RefUpdateType ref_update_type = round_robin_list[round_robin_idx]; | 
|  | int priority_idx = priority_idx_list[round_robin_idx]; | 
|  | int ref_idx = GetRefFrameIdxByPriority(ref_update_type, priority_idx); | 
|  | if (ref_idx != -1) { | 
|  | const ReferenceName name = | 
|  | get_ref_name(ref_update_type, priority_idx, used_name_set); | 
|  | assert(name != ReferenceName::kNoneFrame); | 
|  | used_name_set.insert(name); | 
|  | ReferenceFrame ref_frame = { ref_idx, name }; | 
|  | ref_frame_list.push_back(ref_frame); | 
|  | ++ref_frame_count; | 
|  | ++priority_idx_list[round_robin_idx]; | 
|  | } | 
|  | round_robin_idx = (round_robin_idx + 1) % round_robin_size; | 
|  | } | 
|  | return ref_frame_list; | 
|  | } | 
|  |  | 
|  | void RefFrameManager::UpdateOrder(int global_order_idx) { | 
|  | cur_global_order_idx_ = global_order_idx; | 
|  | if (forward_stack_.empty()) { | 
|  | return; | 
|  | } | 
|  | int ref_idx = forward_stack_.back(); | 
|  | const GopFrame &gf_frame = ref_frame_table_[ref_idx]; | 
|  |  | 
|  | // If the current processing frame is an overlay / show existing frame. | 
|  | if (gf_frame.global_order_idx == global_order_idx) { | 
|  | forward_stack_.pop_back(); | 
|  | if (gf_frame.is_golden_frame) { | 
|  | // high quality frame | 
|  | backward_queue_.push_back(ref_idx); | 
|  | } else { | 
|  | last_queue_.push_back(ref_idx); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | int RefFrameManager::ColocatedRefIdx(int global_order_idx) { | 
|  | if (forward_stack_.empty()) return -1; | 
|  | int ref_idx = forward_stack_.back(); | 
|  | int arf_global_order_idx = ref_frame_table_[ref_idx].global_order_idx; | 
|  | if (arf_global_order_idx == global_order_idx) { | 
|  | return ref_idx; | 
|  | } | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | static RefUpdateType infer_ref_update_type(const GopFrame &gop_frame, | 
|  | int cur_global_order_idx) { | 
|  | if (gop_frame.global_order_idx > cur_global_order_idx) { | 
|  | return RefUpdateType::kForward; | 
|  | } | 
|  | if (gop_frame.is_golden_frame) { | 
|  | return RefUpdateType::kBackward; | 
|  | } | 
|  | if (gop_frame.encode_ref_mode == EncodeRefMode::kShowExisting || | 
|  | gop_frame.encode_ref_mode == EncodeRefMode::kOverlay) { | 
|  | return RefUpdateType::kNone; | 
|  | } | 
|  | return RefUpdateType::kLast; | 
|  | } | 
|  |  | 
|  | using PrimaryRefKey = std::tuple<int,   // abs layer_depth delta | 
|  | bool,  // is_key_frame differs | 
|  | bool,  // is_golden_frame differs | 
|  | bool,  // is_arf_frame differs | 
|  | bool,  // is_show_frame differs | 
|  | bool,  // encode_ref_mode differs | 
|  | int>;  // abs order_idx delta | 
|  |  | 
|  | // Generate PrimaryRefKey based on abs layer_depth delta, | 
|  | // frame flags and abs order_idx delta. These are the fields that will | 
|  | // be used to pick the primary reference frame for probability model | 
|  | static PrimaryRefKey get_primary_ref_key(const GopFrame &cur_frame, | 
|  | const GopFrame &ref_frame) { | 
|  | return std::make_tuple(abs(cur_frame.layer_depth - ref_frame.layer_depth), | 
|  | cur_frame.is_key_frame != ref_frame.is_key_frame, | 
|  | cur_frame.is_golden_frame != ref_frame.is_golden_frame, | 
|  | cur_frame.is_arf_frame != ref_frame.is_arf_frame, | 
|  | cur_frame.is_show_frame != ref_frame.is_show_frame, | 
|  | cur_frame.encode_ref_mode != ref_frame.encode_ref_mode, | 
|  | abs(cur_frame.order_idx - ref_frame.order_idx)); | 
|  | } | 
|  |  | 
|  | // Pick primary_ref_idx for probability model. | 
|  | ReferenceFrame RefFrameManager::GetPrimaryRefFrame( | 
|  | const GopFrame &gop_frame) const { | 
|  | assert(gop_frame.is_valid); | 
|  | std::vector<std::pair<PrimaryRefKey, int>> candidate_list; | 
|  | for (auto &ref_frame_in_gop_frame : gop_frame.ref_frame_list) { | 
|  | const GopFrame &ref_frame = ref_frame_table_[ref_frame_in_gop_frame.index]; | 
|  | if (ref_frame.is_valid) { | 
|  | assert(ref_frame_in_gop_frame.index == ref_frame.update_ref_idx); | 
|  | PrimaryRefKey key = get_primary_ref_key(gop_frame, ref_frame); | 
|  | std::pair<PrimaryRefKey, int> candidate = { | 
|  | key, ref_frame_in_gop_frame.index | 
|  | }; | 
|  | candidate_list.push_back(candidate); | 
|  | } | 
|  | } | 
|  |  | 
|  | std::sort(candidate_list.begin(), candidate_list.end()); | 
|  |  | 
|  | ReferenceFrame ref_frame = { -1, ReferenceName::kNoneFrame }; | 
|  | assert(candidate_list.size() == gop_frame.ref_frame_list.size()); | 
|  | if (!candidate_list.empty()) { | 
|  | int ref_idx = candidate_list[0].second; | 
|  | for (const auto &frame : gop_frame.ref_frame_list) { | 
|  | if (frame.index == ref_idx) { | 
|  | ref_frame = frame; | 
|  | } | 
|  | } | 
|  | } | 
|  | return ref_frame; | 
|  | } | 
|  |  | 
|  | void RefFrameManager::UpdateRefFrameTable(GopFrame *gop_frame) { | 
|  | allow_two_fwd_frames_ = | 
|  | (max_ref_frames_ - !!GetRefFrameCountByType(RefUpdateType::kBackward) - | 
|  | !!GetRefFrameCountByType(RefUpdateType::kLast)) >= 2; | 
|  | gop_frame->ref_frame_list = GetRefFrameListByPriority(); | 
|  | gop_frame->primary_ref_frame = GetPrimaryRefFrame(*gop_frame); | 
|  | gop_frame->colocated_ref_idx = ColocatedRefIdx(gop_frame->global_order_idx); | 
|  |  | 
|  | if (gop_frame->is_show_frame) { | 
|  | UpdateOrder(gop_frame->global_order_idx); | 
|  | } | 
|  | // Call infer_ref_update_type() after UpdateOrder() so that | 
|  | // cur_global_order_idx_ is up-to-date | 
|  | RefUpdateType ref_update_type = | 
|  | infer_ref_update_type(*gop_frame, cur_global_order_idx_); | 
|  | if (ref_update_type == RefUpdateType::kNone) { | 
|  | gop_frame->update_ref_idx = -1; | 
|  | } else { | 
|  | const int ref_idx = AllocateRefIdx(); | 
|  | gop_frame->update_ref_idx = ref_idx; | 
|  | switch (ref_update_type) { | 
|  | case RefUpdateType::kForward: forward_stack_.push_back(ref_idx); break; | 
|  | case RefUpdateType::kBackward: backward_queue_.push_back(ref_idx); break; | 
|  | case RefUpdateType::kLast: last_queue_.push_back(ref_idx); break; | 
|  | case RefUpdateType::kNone: break; | 
|  | } | 
|  | ref_frame_table_[ref_idx] = *gop_frame; | 
|  | } | 
|  | } | 
|  |  | 
|  | }  // namespace aom |