• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2022, Alliance for Open Media. All rights reserved
3  *
4  * This source code is subject to the terms of the BSD 2 Clause License and
5  * the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
6  * was not distributed with this source code in the LICENSE file, you can
7  * obtain it at www.aomedia.org/license/software. If the Alliance for Open
8  * Media Patent License 1.0 was not distributed with this source code in the
9  * PATENTS file, you can obtain it at www.aomedia.org/license/patent.
10  */
11 
12 #include <algorithm>
13 #include <set>
14 #include <utility>
15 #include <tuple>
16 #include <vector>
17 
18 #include "av1/qmode_rc/reference_manager.h"
19 #include "av1/qmode_rc/ratectrl_qmode.h"
20 
21 namespace aom {
22 
Reset()23 void RefFrameManager::Reset() {
24   free_ref_idx_list_.clear();
25   for (int i = 0; i < static_cast<int>(ref_frame_table_.size()); ++i) {
26     free_ref_idx_list_.push_back(i);
27     ref_frame_table_[i] = GopFrameInvalid();
28   }
29   forward_stack_.clear();
30   backward_queue_.clear();
31   last_queue_.clear();
32 }
33 
AllocateRefIdx()34 int RefFrameManager::AllocateRefIdx() {
35   if (free_ref_idx_list_.empty()) {
36     size_t backward_size = backward_queue_.size();
37     size_t last_size = last_queue_.size();
38     if (last_size >= backward_size) {
39       int ref_idx = last_queue_.front();
40       last_queue_.pop_front();
41       free_ref_idx_list_.push_back(ref_idx);
42     } else {
43       int ref_idx = backward_queue_.front();
44       backward_queue_.pop_front();
45       free_ref_idx_list_.push_back(ref_idx);
46     }
47   }
48 
49   int ref_idx = free_ref_idx_list_.front();
50   free_ref_idx_list_.pop_front();
51   return ref_idx;
52 }
53 
GetRefFrameCountByType(RefUpdateType ref_update_type) const54 int RefFrameManager::GetRefFrameCountByType(
55     RefUpdateType ref_update_type) const {
56   size_t cnt = 0;
57   switch (ref_update_type) {
58     case RefUpdateType::kForward: cnt = forward_stack_.size(); break;
59     case RefUpdateType::kBackward: cnt = backward_queue_.size(); break;
60     case RefUpdateType::kLast: cnt = last_queue_.size(); break;
61     case RefUpdateType::kNone: cnt = 0; break;
62   }
63   return static_cast<int>(cnt);
64 }
65 
GetRefFrameCount() const66 int RefFrameManager::GetRefFrameCount() const {
67   return GetRefFrameCountByType(RefUpdateType::kForward) +
68          GetRefFrameCountByType(RefUpdateType::kBackward) +
69          GetRefFrameCountByType(RefUpdateType::kLast);
70 }
71 
72 // TODO(angiebird): Add unit test.
73 // Find the ref_idx corresponding to a ref_update_type.
74 // Return -1 if no ref frame is found.
75 // The priority_idx indicate closeness between the current frame and
76 // the ref frame in display order.
77 // For example, ref_update_type == kForward and priority_idx == 0 means
78 // find the closest ref frame in forward_stack_.
GetRefFrameIdxByPriority(RefUpdateType ref_update_type,int priority_idx) const79 int RefFrameManager::GetRefFrameIdxByPriority(RefUpdateType ref_update_type,
80                                               int priority_idx) const {
81   if (ref_update_type == RefUpdateType::kForward) {
82     int size = static_cast<int>(forward_stack_.size());
83     // When two or more forward reference frames can be used, first get
84     // the highest quality one as the ARF, then going from nearest to
85     // the more distant ones in the forward reference frame list.
86     if (priority_idx < size) {
87       if (allow_two_fwd_frames_) {
88         if (priority_idx == 0) return forward_stack_[0];
89         return forward_stack_[size - priority_idx];
90       }
91 
92       // Handle the special case where only one forward reference frame
93       // can be used. In this setting, we prefer the nearest frame.
94       return forward_stack_[size - 1 - priority_idx];
95     }
96   } else if (ref_update_type == RefUpdateType::kBackward) {
97     int size = static_cast<int>(backward_queue_.size());
98     if (priority_idx < size) {
99       return backward_queue_[size - priority_idx - 1];
100     }
101   } else if (ref_update_type == RefUpdateType::kLast) {
102     int size = static_cast<int>(last_queue_.size());
103     if (priority_idx < size) {
104       return last_queue_[size - priority_idx - 1];
105     }
106   }
107   return -1;
108 }
109 
110 // The priority_idx indicate closeness between the current frame and
111 // the ref frame in display order.
112 // For example, ref_update_type == kForward and priority_idx == 0 means
113 // find the closest ref frame in forward_stack_.
GetRefFrameByPriority(RefUpdateType ref_update_type,int priority_idx) const114 GopFrame RefFrameManager::GetRefFrameByPriority(RefUpdateType ref_update_type,
115                                                 int priority_idx) const {
116   int ref_idx = GetRefFrameIdxByPriority(ref_update_type, priority_idx);
117   if (ref_idx == -1) {
118     return GopFrameInvalid();
119   }
120   assert(ref_frame_table_[ref_idx].update_ref_idx == ref_idx);
121   return ref_frame_table_[ref_idx];
122 }
123 
GetRefFrameByIndex(int ref_idx) const124 GopFrame RefFrameManager::GetRefFrameByIndex(int ref_idx) const {
125   return ref_frame_table_[ref_idx];
126 }
127 
get_ref_name(RefUpdateType ref_update_type,int priority_idx,const std::set<ReferenceName> & used_name_set)128 ReferenceName get_ref_name(RefUpdateType ref_update_type, int priority_idx,
129                            const std::set<ReferenceName> &used_name_set) {
130   // TODO(angiebird): Find the better way to assign name lists.
131   // Maybe sort the names based on how frequent each name is being used in the
132   // past?
133   const std::vector<ReferenceName> forward_name_list{
134     ReferenceName::kAltrefFrame,  ReferenceName::kBwdrefFrame,
135     ReferenceName::kAltref2Frame, ReferenceName::kGoldenFrame,
136     ReferenceName::kLast3Frame,   ReferenceName::kLast2Frame,
137     ReferenceName::kLastFrame
138   };
139   const std::vector<ReferenceName> backward_name_list{
140     ReferenceName::kGoldenFrame, ReferenceName::kLastFrame,
141     ReferenceName::kLast2Frame,  ReferenceName::kLast3Frame,
142     ReferenceName::kBwdrefFrame, ReferenceName::kAltref2Frame,
143     ReferenceName::kAltrefFrame
144   };
145   const std::vector<ReferenceName> last_name_list{
146     ReferenceName::kLastFrame,   ReferenceName::kLast2Frame,
147     ReferenceName::kLast3Frame,  ReferenceName::kGoldenFrame,
148     ReferenceName::kBwdrefFrame, ReferenceName::kAltref2Frame,
149     ReferenceName::kAltrefFrame
150   };
151 
152   const std::vector<ReferenceName> *name_list = nullptr;
153   switch (ref_update_type) {
154     case RefUpdateType::kForward: name_list = &forward_name_list; break;
155     case RefUpdateType::kBackward: name_list = &backward_name_list; break;
156     case RefUpdateType::kLast: name_list = &last_name_list; break;
157     case RefUpdateType::kNone: break;
158   }
159 
160   if (name_list) {
161     const int name_list_size = static_cast<int>(name_list->size());
162     for (int idx = priority_idx; idx < name_list_size; ++idx) {
163       ReferenceName ref_name = name_list->at(idx);
164       bool not_used = used_name_set.find(ref_name) == used_name_set.end();
165       if (not_used) return ref_name;
166     }
167   }
168   return ReferenceName::kNoneFrame;
169 }
170 
171 // Generate a list of available reference frames in priority order for the
172 // current to-be-coded frame. The list size should be less or equal to the size
173 // of ref_frame_table_. The reference frames with smaller indices are more
174 // likely to be a good reference frame. Therefore, they should be prioritized
175 // when the reference frame count is limited. For example, if we plan to use 3
176 // reference frames, we should choose ref_frame_list[0], ref_frame_list[1] and
177 // ref_frame_list[2].
GetRefFrameListByPriority() const178 std::vector<ReferenceFrame> RefFrameManager::GetRefFrameListByPriority() const {
179   constexpr int round_robin_size = 3;
180   const std::vector<RefUpdateType> round_robin_list{ RefUpdateType::kForward,
181                                                      RefUpdateType::kBackward,
182                                                      RefUpdateType::kLast };
183   std::vector<int> priority_idx_list(round_robin_size, 0);
184   int available_ref_frames = GetRefFrameCount();
185   std::vector<ReferenceFrame> ref_frame_list;
186   int ref_frame_count = 0;
187   int round_robin_idx = 0;
188 
189   std::set<ReferenceName> used_name_set;
190   while (ref_frame_count < available_ref_frames &&
191          ref_frame_count < max_ref_frames_) {
192     const RefUpdateType ref_update_type = round_robin_list[round_robin_idx];
193     int priority_idx = priority_idx_list[round_robin_idx];
194     int ref_idx = GetRefFrameIdxByPriority(ref_update_type, priority_idx);
195     if (ref_idx != -1) {
196       const ReferenceName name =
197           get_ref_name(ref_update_type, priority_idx, used_name_set);
198       assert(name != ReferenceName::kNoneFrame);
199       used_name_set.insert(name);
200       ReferenceFrame ref_frame = { ref_idx, name };
201       ref_frame_list.push_back(ref_frame);
202       ++ref_frame_count;
203       ++priority_idx_list[round_robin_idx];
204     }
205     round_robin_idx = (round_robin_idx + 1) % round_robin_size;
206   }
207   return ref_frame_list;
208 }
209 
UpdateOrder(int global_order_idx)210 void RefFrameManager::UpdateOrder(int global_order_idx) {
211   cur_global_order_idx_ = global_order_idx;
212   if (forward_stack_.empty()) {
213     return;
214   }
215   int ref_idx = forward_stack_.back();
216   const GopFrame &gf_frame = ref_frame_table_[ref_idx];
217 
218   // If the current processing frame is an overlay / show existing frame.
219   if (gf_frame.global_order_idx == global_order_idx) {
220     forward_stack_.pop_back();
221     if (gf_frame.is_golden_frame) {
222       // high quality frame
223       backward_queue_.push_back(ref_idx);
224     } else {
225       last_queue_.push_back(ref_idx);
226     }
227   }
228 }
229 
ColocatedRefIdx(int global_order_idx)230 int RefFrameManager::ColocatedRefIdx(int global_order_idx) {
231   if (forward_stack_.empty()) return -1;
232   int ref_idx = forward_stack_.back();
233   int arf_global_order_idx = ref_frame_table_[ref_idx].global_order_idx;
234   if (arf_global_order_idx == global_order_idx) {
235     return ref_idx;
236   }
237   return -1;
238 }
239 
infer_ref_update_type(const GopFrame & gop_frame,int cur_global_order_idx)240 static RefUpdateType infer_ref_update_type(const GopFrame &gop_frame,
241                                            int cur_global_order_idx) {
242   if (gop_frame.global_order_idx > cur_global_order_idx) {
243     return RefUpdateType::kForward;
244   }
245   if (gop_frame.is_golden_frame) {
246     return RefUpdateType::kBackward;
247   }
248   if (gop_frame.encode_ref_mode == EncodeRefMode::kShowExisting ||
249       gop_frame.encode_ref_mode == EncodeRefMode::kOverlay) {
250     return RefUpdateType::kNone;
251   }
252   return RefUpdateType::kLast;
253 }
254 
255 using PrimaryRefKey = std::tuple<int,   // abs layer_depth delta
256                                  bool,  // is_key_frame differs
257                                  bool,  // is_golden_frame differs
258                                  bool,  // is_arf_frame differs
259                                  bool,  // is_show_frame differs
260                                  bool,  // encode_ref_mode differs
261                                  int>;  // abs order_idx delta
262 
263 // Generate PrimaryRefKey based on abs layer_depth delta,
264 // frame flags and abs order_idx delta. These are the fields that will
265 // be used to pick the primary reference frame for probability model
get_primary_ref_key(const GopFrame & cur_frame,const GopFrame & ref_frame)266 static PrimaryRefKey get_primary_ref_key(const GopFrame &cur_frame,
267                                          const GopFrame &ref_frame) {
268   return std::make_tuple(abs(cur_frame.layer_depth - ref_frame.layer_depth),
269                          cur_frame.is_key_frame != ref_frame.is_key_frame,
270                          cur_frame.is_golden_frame != ref_frame.is_golden_frame,
271                          cur_frame.is_arf_frame != ref_frame.is_arf_frame,
272                          cur_frame.is_show_frame != ref_frame.is_show_frame,
273                          cur_frame.encode_ref_mode != ref_frame.encode_ref_mode,
274                          abs(cur_frame.order_idx - ref_frame.order_idx));
275 }
276 
277 // Pick primary_ref_idx for probability model.
GetPrimaryRefFrame(const GopFrame & gop_frame) const278 ReferenceFrame RefFrameManager::GetPrimaryRefFrame(
279     const GopFrame &gop_frame) const {
280   assert(gop_frame.is_valid);
281   std::vector<std::pair<PrimaryRefKey, int>> candidate_list;
282   for (auto &ref_frame_in_gop_frame : gop_frame.ref_frame_list) {
283     const GopFrame &ref_frame = ref_frame_table_[ref_frame_in_gop_frame.index];
284     if (ref_frame.is_valid) {
285       assert(ref_frame_in_gop_frame.index == ref_frame.update_ref_idx);
286       PrimaryRefKey key = get_primary_ref_key(gop_frame, ref_frame);
287       std::pair<PrimaryRefKey, int> candidate = {
288         key, ref_frame_in_gop_frame.index
289       };
290       candidate_list.push_back(candidate);
291     }
292   }
293 
294   std::sort(candidate_list.begin(), candidate_list.end());
295 
296   ReferenceFrame ref_frame = { -1, ReferenceName::kNoneFrame };
297   assert(candidate_list.size() == gop_frame.ref_frame_list.size());
298   if (!candidate_list.empty()) {
299     int ref_idx = candidate_list[0].second;
300     for (const auto &frame : gop_frame.ref_frame_list) {
301       if (frame.index == ref_idx) {
302         ref_frame = frame;
303       }
304     }
305   }
306   return ref_frame;
307 }
308 
UpdateRefFrameTable(GopFrame * gop_frame)309 void RefFrameManager::UpdateRefFrameTable(GopFrame *gop_frame) {
310   allow_two_fwd_frames_ =
311       (max_ref_frames_ - !!GetRefFrameCountByType(RefUpdateType::kBackward) -
312        !!GetRefFrameCountByType(RefUpdateType::kLast)) >= 2;
313   gop_frame->ref_frame_list = GetRefFrameListByPriority();
314   gop_frame->primary_ref_frame = GetPrimaryRefFrame(*gop_frame);
315   gop_frame->colocated_ref_idx = ColocatedRefIdx(gop_frame->global_order_idx);
316 
317   if (gop_frame->is_show_frame) {
318     UpdateOrder(gop_frame->global_order_idx);
319   }
320   // Call infer_ref_update_type() after UpdateOrder() so that
321   // cur_global_order_idx_ is up-to-date
322   RefUpdateType ref_update_type =
323       infer_ref_update_type(*gop_frame, cur_global_order_idx_);
324   if (ref_update_type == RefUpdateType::kNone) {
325     gop_frame->update_ref_idx = -1;
326   } else {
327     const int ref_idx = AllocateRefIdx();
328     gop_frame->update_ref_idx = ref_idx;
329     switch (ref_update_type) {
330       case RefUpdateType::kForward: forward_stack_.push_back(ref_idx); break;
331       case RefUpdateType::kBackward: backward_queue_.push_back(ref_idx); break;
332       case RefUpdateType::kLast: last_queue_.push_back(ref_idx); break;
333       case RefUpdateType::kNone: break;
334     }
335     ref_frame_table_[ref_idx] = *gop_frame;
336   }
337 }
338 
339 }  // namespace aom
340