xref: /aosp_15_r20/external/libaom/test/error_resilience_test.cc (revision 77c1e3ccc04c968bd2bc212e87364f250e820521)
1 /*
2  * Copyright (c) 2016, 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 "gtest/gtest.h"
13 #include "test/codec_factory.h"
14 #include "test/encode_test_driver.h"
15 #include "test/i420_video_source.h"
16 #include "test/util.h"
17 
18 namespace {
19 
20 const int kMaxErrorFrames = 12;
21 const int kMaxInvisibleErrorFrames = 12;
22 const int kMaxDroppableFrames = 12;
23 const int kMaxErrorResilientFrames = 12;
24 const int kMaxNoMFMVFrames = 12;
25 const int kMaxPrimRefNoneFrames = 12;
26 const int kMaxSFrames = 12;
27 const int kCpuUsed = 1;
28 
29 class ErrorResilienceTestLarge
30     : public ::libaom_test::CodecTestWith2Params<libaom_test::TestMode, int>,
31       public ::libaom_test::EncoderTest {
32  protected:
ErrorResilienceTestLarge()33   ErrorResilienceTestLarge()
34       : EncoderTest(GET_PARAM(0)), psnr_(0.0), nframes_(0), mismatch_psnr_(0.0),
35         mismatch_nframes_(0), encoding_mode_(GET_PARAM(1)), allow_mismatch_(0),
36         enable_altref_(GET_PARAM(2)) {
37     Reset();
38   }
39 
40   ~ErrorResilienceTestLarge() override = default;
41 
Reset()42   void Reset() {
43     error_nframes_ = 0;
44     invisible_error_nframes_ = 0;
45     droppable_nframes_ = 0;
46     error_resilient_nframes_ = 0;
47     nomfmv_nframes_ = 0;
48     prim_ref_none_nframes_ = 0;
49     s_nframes_ = 0;
50   }
51 
SetupEncoder(int bitrate,int lag)52   void SetupEncoder(int bitrate, int lag) {
53     const aom_rational timebase = { 33333333, 1000000000 };
54     cfg_.g_timebase = timebase;
55     cfg_.rc_target_bitrate = bitrate;
56     cfg_.kf_mode = AOM_KF_DISABLED;
57     cfg_.g_lag_in_frames = lag;
58     init_flags_ = AOM_CODEC_USE_PSNR;
59   }
60 
SetUp()61   void SetUp() override { InitializeConfig(encoding_mode_); }
62 
BeginPassHook(unsigned int)63   void BeginPassHook(unsigned int /*pass*/) override {
64     psnr_ = 0.0;
65     nframes_ = 0;
66     decoded_nframes_ = 0;
67     mismatch_psnr_ = 0.0;
68     mismatch_nframes_ = 0;
69   }
70 
PSNRPktHook(const aom_codec_cx_pkt_t * pkt)71   void PSNRPktHook(const aom_codec_cx_pkt_t *pkt) override {
72     psnr_ += pkt->data.psnr.psnr[0];
73     nframes_++;
74   }
75 
PreEncodeFrameHook(libaom_test::VideoSource * video,libaom_test::Encoder * encoder)76   void PreEncodeFrameHook(libaom_test::VideoSource *video,
77                           libaom_test::Encoder *encoder) override {
78     if (video->frame() == 0) {
79       encoder->Control(AOME_SET_CPUUSED, kCpuUsed);
80       encoder->Control(AOME_SET_ENABLEAUTOALTREF, enable_altref_);
81     }
82     frame_flags_ &=
83         ~(AOM_EFLAG_NO_UPD_LAST | AOM_EFLAG_NO_UPD_GF | AOM_EFLAG_NO_UPD_ARF |
84           AOM_EFLAG_NO_REF_FRAME_MVS | AOM_EFLAG_ERROR_RESILIENT |
85           AOM_EFLAG_SET_S_FRAME | AOM_EFLAG_SET_PRIMARY_REF_NONE);
86     if (droppable_nframes_ > 0 &&
87         (cfg_.g_pass == AOM_RC_LAST_PASS || cfg_.g_pass == AOM_RC_ONE_PASS)) {
88       for (unsigned int i = 0; i < droppable_nframes_; ++i) {
89         if (droppable_frames_[i] == video->frame()) {
90           std::cout << "             Encoding droppable frame: "
91                     << droppable_frames_[i] << "\n";
92           frame_flags_ |= (AOM_EFLAG_NO_UPD_LAST | AOM_EFLAG_NO_UPD_GF |
93                            AOM_EFLAG_NO_UPD_ARF);
94           break;
95         }
96       }
97     }
98 
99     if (error_resilient_nframes_ > 0 &&
100         (cfg_.g_pass == AOM_RC_LAST_PASS || cfg_.g_pass == AOM_RC_ONE_PASS)) {
101       for (unsigned int i = 0; i < error_resilient_nframes_; ++i) {
102         if (error_resilient_frames_[i] == video->frame()) {
103           std::cout << "             Encoding error_resilient frame: "
104                     << error_resilient_frames_[i] << "\n";
105           frame_flags_ |= AOM_EFLAG_ERROR_RESILIENT;
106           break;
107         }
108       }
109     }
110 
111     if (nomfmv_nframes_ > 0 &&
112         (cfg_.g_pass == AOM_RC_LAST_PASS || cfg_.g_pass == AOM_RC_ONE_PASS)) {
113       for (unsigned int i = 0; i < nomfmv_nframes_; ++i) {
114         if (nomfmv_frames_[i] == video->frame()) {
115           std::cout << "             Encoding no mfmv frame: "
116                     << nomfmv_frames_[i] << "\n";
117           frame_flags_ |= AOM_EFLAG_NO_REF_FRAME_MVS;
118           break;
119         }
120       }
121     }
122 
123     if (prim_ref_none_nframes_ > 0 &&
124         (cfg_.g_pass == AOM_RC_LAST_PASS || cfg_.g_pass == AOM_RC_ONE_PASS)) {
125       for (unsigned int i = 0; i < prim_ref_none_nframes_; ++i) {
126         if (prim_ref_none_frames_[i] == video->frame()) {
127           std::cout << "             Encoding no PRIMARY_REF_NONE frame: "
128                     << prim_ref_none_frames_[i] << "\n";
129           frame_flags_ |= AOM_EFLAG_SET_PRIMARY_REF_NONE;
130           break;
131         }
132       }
133     }
134 
135     encoder->Control(AV1E_SET_S_FRAME_MODE, 0);
136     if (s_nframes_ > 0 &&
137         (cfg_.g_pass == AOM_RC_LAST_PASS || cfg_.g_pass == AOM_RC_ONE_PASS)) {
138       for (unsigned int i = 0; i < s_nframes_; ++i) {
139         if (s_frames_[i] == video->frame()) {
140           std::cout << "             Encoding S frame: " << s_frames_[i]
141                     << "\n";
142           frame_flags_ |= AOM_EFLAG_SET_S_FRAME;
143           break;
144         }
145       }
146     }
147   }
148 
FramePktHook(const aom_codec_cx_pkt_t * pkt)149   void FramePktHook(const aom_codec_cx_pkt_t *pkt) override {
150     // Check that the encode frame flags are correctly reflected
151     // in the output frame flags.
152     const int encode_flags = pkt->data.frame.flags >> 16;
153     if ((encode_flags & (AOM_EFLAG_NO_UPD_LAST | AOM_EFLAG_NO_UPD_GF |
154                          AOM_EFLAG_NO_UPD_ARF)) ==
155         (AOM_EFLAG_NO_UPD_LAST | AOM_EFLAG_NO_UPD_GF | AOM_EFLAG_NO_UPD_ARF)) {
156       ASSERT_EQ(pkt->data.frame.flags & AOM_FRAME_IS_DROPPABLE,
157                 AOM_FRAME_IS_DROPPABLE);
158     }
159     if (encode_flags & AOM_EFLAG_SET_S_FRAME) {
160       ASSERT_EQ(pkt->data.frame.flags & AOM_FRAME_IS_SWITCH,
161                 AOM_FRAME_IS_SWITCH);
162     }
163     if (encode_flags & AOM_EFLAG_ERROR_RESILIENT) {
164       ASSERT_EQ(pkt->data.frame.flags & AOM_FRAME_IS_ERROR_RESILIENT,
165                 AOM_FRAME_IS_ERROR_RESILIENT);
166     }
167   }
168 
GetAveragePsnr() const169   double GetAveragePsnr() const {
170     if (nframes_) return psnr_ / nframes_;
171     return 0.0;
172   }
173 
GetAverageMismatchPsnr() const174   double GetAverageMismatchPsnr() const {
175     if (mismatch_nframes_) return mismatch_psnr_ / mismatch_nframes_;
176     return 0.0;
177   }
178 
DoDecode() const179   bool DoDecode() const override {
180     if (error_nframes_ > 0 &&
181         (cfg_.g_pass == AOM_RC_LAST_PASS || cfg_.g_pass == AOM_RC_ONE_PASS)) {
182       for (unsigned int i = 0; i < error_nframes_; ++i) {
183         if (error_frames_[i] == nframes_ - 1) {
184           std::cout << "             Skipping decoding frame: "
185                     << error_frames_[i] << "\n";
186           return false;
187         }
188       }
189     }
190     return true;
191   }
192 
DoDecodeInvisible() const193   bool DoDecodeInvisible() const override {
194     if (invisible_error_nframes_ > 0 &&
195         (cfg_.g_pass == AOM_RC_LAST_PASS || cfg_.g_pass == AOM_RC_ONE_PASS)) {
196       for (unsigned int i = 0; i < invisible_error_nframes_; ++i) {
197         if (invisible_error_frames_[i] == nframes_ - 1) {
198           std::cout << "             Skipping decoding all invisible frames in "
199                        "frame pkt: "
200                     << invisible_error_frames_[i] << "\n";
201           return false;
202         }
203       }
204     }
205     return true;
206   }
207 
MismatchHook(const aom_image_t * img1,const aom_image_t * img2)208   void MismatchHook(const aom_image_t *img1, const aom_image_t *img2) override {
209     if (allow_mismatch_) {
210       double mismatch_psnr = compute_psnr(img1, img2);
211       mismatch_psnr_ += mismatch_psnr;
212       ++mismatch_nframes_;
213       // std::cout << "Mismatch frame psnr: " << mismatch_psnr << "\n";
214     } else {
215       ::libaom_test::EncoderTest::MismatchHook(img1, img2);
216     }
217   }
218 
DecompressedFrameHook(const aom_image_t & img,aom_codec_pts_t pts)219   void DecompressedFrameHook(const aom_image_t &img,
220                              aom_codec_pts_t pts) override {
221     (void)img;
222     (void)pts;
223     ++decoded_nframes_;
224   }
225 
SetErrorFrames(int num,unsigned int * list)226   void SetErrorFrames(int num, unsigned int *list) {
227     if (num > kMaxErrorFrames)
228       num = kMaxErrorFrames;
229     else if (num < 0)
230       num = 0;
231     error_nframes_ = num;
232     for (unsigned int i = 0; i < error_nframes_; ++i)
233       error_frames_[i] = list[i];
234   }
235 
SetInvisibleErrorFrames(int num,unsigned int * list)236   void SetInvisibleErrorFrames(int num, unsigned int *list) {
237     if (num > kMaxInvisibleErrorFrames)
238       num = kMaxInvisibleErrorFrames;
239     else if (num < 0)
240       num = 0;
241     invisible_error_nframes_ = num;
242     for (unsigned int i = 0; i < invisible_error_nframes_; ++i)
243       invisible_error_frames_[i] = list[i];
244   }
245 
SetDroppableFrames(int num,unsigned int * list)246   void SetDroppableFrames(int num, unsigned int *list) {
247     if (num > kMaxDroppableFrames)
248       num = kMaxDroppableFrames;
249     else if (num < 0)
250       num = 0;
251     droppable_nframes_ = num;
252     for (unsigned int i = 0; i < droppable_nframes_; ++i)
253       droppable_frames_[i] = list[i];
254   }
255 
SetErrorResilientFrames(int num,unsigned int * list)256   void SetErrorResilientFrames(int num, unsigned int *list) {
257     if (num > kMaxErrorResilientFrames)
258       num = kMaxErrorResilientFrames;
259     else if (num < 0)
260       num = 0;
261     error_resilient_nframes_ = num;
262     for (unsigned int i = 0; i < error_resilient_nframes_; ++i)
263       error_resilient_frames_[i] = list[i];
264   }
265 
SetNoMFMVFrames(int num,unsigned int * list)266   void SetNoMFMVFrames(int num, unsigned int *list) {
267     if (num > kMaxNoMFMVFrames)
268       num = kMaxNoMFMVFrames;
269     else if (num < 0)
270       num = 0;
271     nomfmv_nframes_ = num;
272     for (unsigned int i = 0; i < nomfmv_nframes_; ++i)
273       nomfmv_frames_[i] = list[i];
274   }
275 
SetPrimaryRefNoneFrames(int num,unsigned int * list)276   void SetPrimaryRefNoneFrames(int num, unsigned int *list) {
277     if (num > kMaxPrimRefNoneFrames)
278       num = kMaxPrimRefNoneFrames;
279     else if (num < 0)
280       num = 0;
281     prim_ref_none_nframes_ = num;
282     for (unsigned int i = 0; i < prim_ref_none_nframes_; ++i)
283       prim_ref_none_frames_[i] = list[i];
284   }
285 
SetSFrames(int num,unsigned int * list)286   void SetSFrames(int num, unsigned int *list) {
287     if (num > kMaxSFrames)
288       num = kMaxSFrames;
289     else if (num < 0)
290       num = 0;
291     s_nframes_ = num;
292     for (unsigned int i = 0; i < s_nframes_; ++i) s_frames_[i] = list[i];
293   }
294 
GetMismatchFrames()295   unsigned int GetMismatchFrames() { return mismatch_nframes_; }
GetEncodedFrames()296   unsigned int GetEncodedFrames() { return nframes_; }
GetDecodedFrames()297   unsigned int GetDecodedFrames() { return decoded_nframes_; }
298 
SetAllowMismatch(int allow)299   void SetAllowMismatch(int allow) { allow_mismatch_ = allow; }
300 
301  private:
302   double psnr_;
303   unsigned int nframes_;
304   unsigned int decoded_nframes_;
305   unsigned int error_nframes_;
306   unsigned int invisible_error_nframes_;
307   unsigned int droppable_nframes_;
308   unsigned int error_resilient_nframes_;
309   unsigned int nomfmv_nframes_;
310   unsigned int prim_ref_none_nframes_;
311   unsigned int s_nframes_;
312   double mismatch_psnr_;
313   unsigned int mismatch_nframes_;
314   unsigned int error_frames_[kMaxErrorFrames];
315   unsigned int invisible_error_frames_[kMaxInvisibleErrorFrames];
316   unsigned int droppable_frames_[kMaxDroppableFrames];
317   unsigned int error_resilient_frames_[kMaxErrorResilientFrames];
318   unsigned int nomfmv_frames_[kMaxNoMFMVFrames];
319   unsigned int prim_ref_none_frames_[kMaxPrimRefNoneFrames];
320   unsigned int s_frames_[kMaxSFrames];
321   libaom_test::TestMode encoding_mode_;
322   int allow_mismatch_;
323   int enable_altref_;
324 };
325 
TEST_P(ErrorResilienceTestLarge,OnVersusOff)326 TEST_P(ErrorResilienceTestLarge, OnVersusOff) {
327   SetupEncoder(2000, 10);
328   libaom_test::I420VideoSource video("hantro_collage_w352h288.yuv", 352, 288,
329                                      cfg_.g_timebase.den, cfg_.g_timebase.num,
330                                      0, 12);
331 
332   // Global error resilient mode OFF.
333   cfg_.g_error_resilient = 0;
334   ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
335   const double psnr_resilience_off = GetAveragePsnr();
336   EXPECT_GT(psnr_resilience_off, 25.0);
337 
338   Reset();
339   // Error resilient mode ON for certain frames
340   unsigned int num_error_resilient_frames = 5;
341   unsigned int error_resilient_frame_list[] = { 3, 5, 6, 9, 11 };
342   SetErrorResilientFrames(num_error_resilient_frames,
343                           error_resilient_frame_list);
344   ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
345   const double psnr_resilience_on = GetAveragePsnr();
346   EXPECT_GT(psnr_resilience_on, 25.0);
347 
348   // Test that turning on error resilient mode hurts by 10% at most.
349   if (psnr_resilience_off > 0.0) {
350     const double psnr_ratio = psnr_resilience_on / psnr_resilience_off;
351     EXPECT_GE(psnr_ratio, 0.9);
352     EXPECT_LE(psnr_ratio, 1.1);
353   }
354 }
355 
356 // Check for successful decoding and no encoder/decoder mismatch
357 // if we lose (i.e., drop before decoding) a set of droppable
358 // frames (i.e., frames that don't update any reference buffers).
TEST_P(ErrorResilienceTestLarge,DropFramesWithoutRecovery)359 TEST_P(ErrorResilienceTestLarge, DropFramesWithoutRecovery) {
360   if (GET_PARAM(1) == ::libaom_test::kOnePassGood && GET_PARAM(2) == 1) {
361     fprintf(stderr, "Skipping test case #1 because of bug aomedia:3002\n");
362     return;
363   }
364   SetupEncoder(500, 10);
365   libaom_test::I420VideoSource video("hantro_collage_w352h288.yuv", 352, 288,
366                                      cfg_.g_timebase.den, cfg_.g_timebase.num,
367                                      0, 20);
368 
369   // Set an arbitrary set of error frames same as droppable frames.
370   unsigned int num_droppable_frames = 3;
371   unsigned int droppable_frame_list[] = { 5, 11, 13 };
372   SetDroppableFrames(num_droppable_frames, droppable_frame_list);
373   SetErrorFrames(num_droppable_frames, droppable_frame_list);
374   ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
375   // Test that no mismatches have been found
376   std::cout << "             Encoded frames: " << GetEncodedFrames() << "\n";
377   std::cout << "             Decoded frames: " << GetDecodedFrames() << "\n";
378   std::cout << "             Mismatch frames: " << GetMismatchFrames() << "\n";
379   EXPECT_EQ(GetEncodedFrames() - GetDecodedFrames(), num_droppable_frames);
380 }
381 
382 // Check for ParseAbility property of an error-resilient frame.
383 // Encode a frame in error-resilient mode (E-frame), and disallow all
384 // subsequent frames from using MFMV. If frames are dropped before the
385 // E frame, all frames starting from the E frame should be parse-able.
TEST_P(ErrorResilienceTestLarge,ParseAbilityTest)386 TEST_P(ErrorResilienceTestLarge, ParseAbilityTest) {
387   SetupEncoder(500, 10);
388 
389   libaom_test::I420VideoSource video("hantro_collage_w352h288.yuv", 352, 288,
390                                      cfg_.g_timebase.den, cfg_.g_timebase.num,
391                                      0, 15);
392 
393   SetAllowMismatch(1);
394 
395   // Note that an E-frame cannot be forced on a frame that is a
396   // show_existing_frame, or a frame that comes directly after an invisible
397   // frame. Currently, this will cause an assertion failure.
398   // Set an arbitrary error resilient (E) frame
399   unsigned int num_error_resilient_frames = 1;
400   unsigned int error_resilient_frame_list[] = { 8 };
401   SetErrorResilientFrames(num_error_resilient_frames,
402                           error_resilient_frame_list);
403   // Ensure that any invisible frames before the E frame are dropped
404   SetInvisibleErrorFrames(num_error_resilient_frames,
405                           error_resilient_frame_list);
406   // Set all frames after the error resilient frame to not allow MFMV
407   unsigned int num_post_error_resilient_frames = 6;
408   unsigned int post_error_resilient_frame_list[] = { 9, 10, 11, 12, 13, 14 };
409   SetNoMFMVFrames(num_post_error_resilient_frames,
410                   post_error_resilient_frame_list);
411 
412   // Set a few frames before the E frame that are lost (not decoded)
413   unsigned int num_error_frames = 5;
414   unsigned int error_frame_list[] = { 3, 4, 5, 6, 7 };
415   SetErrorFrames(num_error_frames, error_frame_list);
416 
417   ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
418   std::cout << "             Encoded frames: " << GetEncodedFrames() << "\n";
419   std::cout << "             Decoded frames: " << GetDecodedFrames() << "\n";
420   std::cout << "             Mismatch frames: " << GetMismatchFrames() << "\n";
421   EXPECT_EQ(GetEncodedFrames() - GetDecodedFrames(), num_error_frames);
422   // All frames following the E-frame and the E-frame are expected to have
423   // mismatches, but still be parse-able.
424   EXPECT_LE(GetMismatchFrames(), num_post_error_resilient_frames + 1);
425 }
426 
427 // Check for ParseAbility property of an S frame.
428 // Encode an S-frame. If frames are dropped before the S-frame, all frames
429 // starting from the S frame should be parse-able.
TEST_P(ErrorResilienceTestLarge,SFrameTest)430 TEST_P(ErrorResilienceTestLarge, SFrameTest) {
431   SetupEncoder(500, 10);
432 
433   libaom_test::I420VideoSource video("hantro_collage_w352h288.yuv", 352, 288,
434                                      cfg_.g_timebase.den, cfg_.g_timebase.num,
435                                      0, 15);
436 
437   SetAllowMismatch(1);
438 
439   // Note that an S-frame cannot be forced on a frame that is a
440   // show_existing_frame. This issue still needs to be addressed.
441   // Set an arbitrary S-frame
442   unsigned int num_s_frames = 1;
443   unsigned int s_frame_list[] = { 6 };
444   SetSFrames(num_s_frames, s_frame_list);
445   // Ensure that any invisible frames before the S frame are dropped
446   SetInvisibleErrorFrames(num_s_frames, s_frame_list);
447 
448   // Set a few frames before the S frame that are lost (not decoded)
449   unsigned int num_error_frames = 4;
450   unsigned int error_frame_list[] = { 2, 3, 4, 5 };
451   SetErrorFrames(num_error_frames, error_frame_list);
452 
453   ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
454   std::cout << "             Encoded frames: " << GetEncodedFrames() << "\n";
455   std::cout << "             Decoded frames: " << GetDecodedFrames() << "\n";
456   std::cout << "             Mismatch frames: " << GetMismatchFrames() << "\n";
457   EXPECT_EQ(GetEncodedFrames() - GetDecodedFrames(), num_error_frames);
458   // All frames following the S-frame and the S-frame are expected to have
459   // mismatches, but still be parse-able.
460   EXPECT_LE(GetMismatchFrames(), GetEncodedFrames() - s_frame_list[0]);
461 }
462 
463 AV1_INSTANTIATE_TEST_SUITE(ErrorResilienceTestLarge, NONREALTIME_TEST_MODES,
464                            ::testing::Values(0, 1));
465 }  // namespace
466