error_resilience_test.cc 19.6 KB
Newer Older
1
/*
2
 * Copyright (c) 2016, Alliance for Open Media. All rights reserved
Frank Galligan's avatar
Frank Galligan committed
3
 *
4 5 6 7 8 9 10 11
 * 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.
*/

12

13
#include "third_party/googletest/src/include/gtest/gtest.h"
14
#include "test/codec_factory.h"
15 16
#include "test/encode_test_driver.h"
#include "test/i420_video_source.h"
17
#include "test/util.h"
18 19 20

namespace {

21 22
const int kMaxErrorFrames = 12;
const int kMaxDroppableFrames = 12;
23

clang-format's avatar
clang-format committed
24
class ErrorResilienceTestLarge
Yaowu Xu's avatar
Yaowu Xu committed
25 26
    : public ::libaom_test::EncoderTest,
      public ::libaom_test::CodecTestWith2Params<libaom_test::TestMode, bool> {
27
 protected:
28
  ErrorResilienceTestLarge()
clang-format's avatar
clang-format committed
29 30
      : EncoderTest(GET_PARAM(0)), svc_support_(GET_PARAM(2)), psnr_(0.0),
        nframes_(0), mismatch_psnr_(0.0), mismatch_nframes_(0),
31
        encoding_mode_(GET_PARAM(1)) {
32 33
    Reset();
  }
34

35
  virtual ~ErrorResilienceTestLarge() {}
36

37 38 39
  void Reset() {
    error_nframes_ = 0;
    droppable_nframes_ = 0;
Marco's avatar
Marco committed
40
    pattern_switch_ = 0;
41 42
  }

43 44 45 46 47 48 49 50
  virtual void SetUp() {
    InitializeConfig();
    SetMode(encoding_mode_);
  }

  virtual void BeginPassHook(unsigned int /*pass*/) {
    psnr_ = 0.0;
    nframes_ = 0;
51 52
    mismatch_psnr_ = 0.0;
    mismatch_nframes_ = 0;
53 54
  }

Adrian Grange's avatar
Adrian Grange committed
55
  virtual void PSNRPktHook(const aom_codec_cx_pkt_t *pkt) {
56 57 58 59
    psnr_ += pkt->data.psnr.psnr[0];
    nframes_++;
  }

60 61 62 63 64 65
  //
  // Frame flags and layer id for temporal layers.
  // For two layers, test pattern is:
  //   1     3
  // 0    2     .....
  // LAST is updated on base/layer 0, GOLDEN  updated on layer 1.
Marco's avatar
Marco committed
66 67
  // Non-zero pattern_switch parameter means pattern will switch to
  // not using LAST for frame_num >= pattern_switch.
clang-format's avatar
clang-format committed
68
  int SetFrameFlags(int frame_num, int num_temp_layers, int pattern_switch) {
69 70
    int frame_flags = 0;
    if (num_temp_layers == 2) {
clang-format's avatar
clang-format committed
71 72 73 74
      if (frame_num % 2 == 0) {
        if (frame_num < pattern_switch || pattern_switch == 0) {
          // Layer 0: predict from LAST and ARF, update LAST.
          frame_flags =
Adrian Grange's avatar
Adrian Grange committed
75
              AOM_EFLAG_NO_REF_GF | AOM_EFLAG_NO_UPD_GF | AOM_EFLAG_NO_UPD_ARF;
Marco's avatar
Marco committed
76
        } else {
clang-format's avatar
clang-format committed
77
          // Layer 0: predict from GF and ARF, update GF.
Adrian Grange's avatar
Adrian Grange committed
78 79
          frame_flags = AOM_EFLAG_NO_REF_LAST | AOM_EFLAG_NO_UPD_LAST |
                        AOM_EFLAG_NO_UPD_ARF;
clang-format's avatar
clang-format committed
80 81 82 83
        }
      } else {
        if (frame_num < pattern_switch || pattern_switch == 0) {
          // Layer 1: predict from L, GF, and ARF, update GF.
Adrian Grange's avatar
Adrian Grange committed
84
          frame_flags = AOM_EFLAG_NO_UPD_ARF | AOM_EFLAG_NO_UPD_LAST;
clang-format's avatar
clang-format committed
85 86
        } else {
          // Layer 1: predict from GF and ARF, update GF.
Adrian Grange's avatar
Adrian Grange committed
87 88
          frame_flags = AOM_EFLAG_NO_REF_LAST | AOM_EFLAG_NO_UPD_LAST |
                        AOM_EFLAG_NO_UPD_ARF;
Marco's avatar
Marco committed
89
        }
clang-format's avatar
clang-format committed
90
      }
91 92 93 94
    }
    return frame_flags;
  }

Yaowu Xu's avatar
Yaowu Xu committed
95 96
  virtual void PreEncodeFrameHook(libaom_test::VideoSource *video,
                                  ::libaom_test::Encoder *encoder) {
clang-format's avatar
clang-format committed
97
    frame_flags_ &=
Adrian Grange's avatar
Adrian Grange committed
98
        ~(AOM_EFLAG_NO_UPD_LAST | AOM_EFLAG_NO_UPD_GF | AOM_EFLAG_NO_UPD_ARF);
99 100
    // For temporal layer case.
    if (cfg_.ts_number_layers > 1) {
clang-format's avatar
clang-format committed
101 102
      frame_flags_ =
          SetFrameFlags(video->frame(), cfg_.ts_number_layers, pattern_switch_);
103
      for (unsigned int i = 0; i < droppable_nframes_; ++i) {
104
        if (droppable_frames_[i] == video->frame()) {
clang-format's avatar
clang-format committed
105 106
          std::cout << "Encoding droppable frame: " << droppable_frames_[i]
                    << "\n";
107 108
        }
      }
109
    } else {
clang-format's avatar
clang-format committed
110
      if (droppable_nframes_ > 0 &&
Adrian Grange's avatar
Adrian Grange committed
111
          (cfg_.g_pass == AOM_RC_LAST_PASS || cfg_.g_pass == AOM_RC_ONE_PASS)) {
clang-format's avatar
clang-format committed
112 113 114 115
        for (unsigned int i = 0; i < droppable_nframes_; ++i) {
          if (droppable_frames_[i] == video->frame()) {
            std::cout << "Encoding droppable frame: " << droppable_frames_[i]
                      << "\n";
Adrian Grange's avatar
Adrian Grange committed
116 117
            frame_flags_ |= (AOM_EFLAG_NO_UPD_LAST | AOM_EFLAG_NO_UPD_GF |
                             AOM_EFLAG_NO_UPD_ARF);
clang-format's avatar
clang-format committed
118 119 120 121
            return;
          }
        }
      }
122 123 124
    }
  }

125
  double GetAveragePsnr() const {
clang-format's avatar
clang-format committed
126
    if (nframes_) return psnr_ / nframes_;
127 128 129
    return 0.0;
  }

130
  double GetAverageMismatchPsnr() const {
clang-format's avatar
clang-format committed
131
    if (mismatch_nframes_) return mismatch_psnr_ / mismatch_nframes_;
132 133 134 135 136
    return 0.0;
  }

  virtual bool DoDecode() const {
    if (error_nframes_ > 0 &&
Adrian Grange's avatar
Adrian Grange committed
137
        (cfg_.g_pass == AOM_RC_LAST_PASS || cfg_.g_pass == AOM_RC_ONE_PASS)) {
138 139 140 141 142 143 144 145 146 147 148
      for (unsigned int i = 0; i < error_nframes_; ++i) {
        if (error_frames_[i] == nframes_ - 1) {
          std::cout << "             Skipping decoding frame: "
                    << error_frames_[i] << "\n";
          return 0;
        }
      }
    }
    return 1;
  }

Adrian Grange's avatar
Adrian Grange committed
149
  virtual void MismatchHook(const aom_image_t *img1, const aom_image_t *img2) {
150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175
    double mismatch_psnr = compute_psnr(img1, img2);
    mismatch_psnr_ += mismatch_psnr;
    ++mismatch_nframes_;
    // std::cout << "Mismatch frame psnr: " << mismatch_psnr << "\n";
  }

  void SetErrorFrames(int num, unsigned int *list) {
    if (num > kMaxErrorFrames)
      num = kMaxErrorFrames;
    else if (num < 0)
      num = 0;
    error_nframes_ = num;
    for (unsigned int i = 0; i < error_nframes_; ++i)
      error_frames_[i] = list[i];
  }

  void SetDroppableFrames(int num, unsigned int *list) {
    if (num > kMaxDroppableFrames)
      num = kMaxDroppableFrames;
    else if (num < 0)
      num = 0;
    droppable_nframes_ = num;
    for (unsigned int i = 0; i < droppable_nframes_; ++i)
      droppable_frames_[i] = list[i];
  }

clang-format's avatar
clang-format committed
176
  unsigned int GetMismatchFrames() { return mismatch_nframes_; }
177

clang-format's avatar
clang-format committed
178
  void SetPatternSwitch(int frame_switch) { pattern_switch_ = frame_switch; }
Marco's avatar
Marco committed
179

180 181
  bool svc_support_;

182 183 184
 private:
  double psnr_;
  unsigned int nframes_;
185 186
  unsigned int error_nframes_;
  unsigned int droppable_nframes_;
Marco's avatar
Marco committed
187
  unsigned int pattern_switch_;
188 189 190 191
  double mismatch_psnr_;
  unsigned int mismatch_nframes_;
  unsigned int error_frames_[kMaxErrorFrames];
  unsigned int droppable_frames_[kMaxDroppableFrames];
Yaowu Xu's avatar
Yaowu Xu committed
192
  libaom_test::TestMode encoding_mode_;
193 194
};

195
TEST_P(ErrorResilienceTestLarge, OnVersusOff) {
Adrian Grange's avatar
Adrian Grange committed
196
  const aom_rational timebase = { 33333333, 1000000000 };
197 198
  cfg_.g_timebase = timebase;
  cfg_.rc_target_bitrate = 2000;
199
  cfg_.g_lag_in_frames = 10;
200

Adrian Grange's avatar
Adrian Grange committed
201
  init_flags_ = AOM_CODEC_USE_PSNR;
202

Yaowu Xu's avatar
Yaowu Xu committed
203
  libaom_test::I420VideoSource video("hantro_collage_w352h288.yuv", 352, 288,
204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225
                                     timebase.den, timebase.num, 0, 30);

  // Error resilient mode OFF.
  cfg_.g_error_resilient = 0;
  ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
  const double psnr_resilience_off = GetAveragePsnr();
  EXPECT_GT(psnr_resilience_off, 25.0);

  // Error resilient mode ON.
  cfg_.g_error_resilient = 1;
  ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
  const double psnr_resilience_on = GetAveragePsnr();
  EXPECT_GT(psnr_resilience_on, 25.0);

  // Test that turning on error resilient mode hurts by 10% at most.
  if (psnr_resilience_off > 0.0) {
    const double psnr_ratio = psnr_resilience_on / psnr_resilience_off;
    EXPECT_GE(psnr_ratio, 0.9);
    EXPECT_LE(psnr_ratio, 1.1);
  }
}

226 227 228 229
// Check for successful decoding and no encoder/decoder mismatch
// if we lose (i.e., drop before decoding) a set of droppable
// frames (i.e., frames that don't update any reference buffers).
// Check both isolated and consecutive loss.
230
TEST_P(ErrorResilienceTestLarge, DropFramesWithoutRecovery) {
Adrian Grange's avatar
Adrian Grange committed
231
  const aom_rational timebase = { 33333333, 1000000000 };
232
  cfg_.g_timebase = timebase;
233
  cfg_.rc_target_bitrate = 500;
234 235 236
  // FIXME(debargha): Fix this to work for any lag.
  // Currently this test only works for lag = 0
  cfg_.g_lag_in_frames = 0;
237

Adrian Grange's avatar
Adrian Grange committed
238
  init_flags_ = AOM_CODEC_USE_PSNR;
239

Yaowu Xu's avatar
Yaowu Xu committed
240
  libaom_test::I420VideoSource video("hantro_collage_w352h288.yuv", 352, 288,
241
                                     timebase.den, timebase.num, 0, 40);
242 243 244

  // Error resilient mode ON.
  cfg_.g_error_resilient = 1;
Adrian Grange's avatar
Adrian Grange committed
245
  cfg_.kf_mode = AOM_KF_DISABLED;
246 247 248 249 250

  // Set an arbitrary set of error frames same as droppable frames.
  // In addition to isolated loss/drop, add a long consecutive series
  // (of size 9) of dropped frames.
  unsigned int num_droppable_frames = 11;
clang-format's avatar
clang-format committed
251 252
  unsigned int droppable_frame_list[] = { 5,  16, 22, 23, 24, 25,
                                          26, 27, 28, 29, 30 };
253 254 255 256
  SetDroppableFrames(num_droppable_frames, droppable_frame_list);
  SetErrorFrames(num_droppable_frames, droppable_frame_list);
  ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
  // Test that no mismatches have been found
clang-format's avatar
clang-format committed
257 258
  std::cout << "             Mismatch frames: " << GetMismatchFrames() << "\n";
  EXPECT_EQ(GetMismatchFrames(), (unsigned int)0);
259

260
  // Reset previously set of error/droppable frames.
261 262
  Reset();

263 264 265 266 267
#if 0
  // TODO(jkoleszar): This test is disabled for the time being as too
  // sensitive. It's not clear how to set a reasonable threshold for
  // this behavior.

268 269 270 271 272
  // Now set an arbitrary set of error frames that are non-droppable
  unsigned int num_error_frames = 3;
  unsigned int error_frame_list[] = {3, 10, 20};
  SetErrorFrames(num_error_frames, error_frame_list);
  ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
273

274 275 276 277 278 279 280 281
  // Test that dropping an arbitrary set of inter frames does not hurt too much
  // Note the Average Mismatch PSNR is the average of the PSNR between
  // decoded frame and encoder's version of the same frame for all frames
  // with mismatch.
  const double psnr_resilience_mismatch = GetAverageMismatchPsnr();
  std::cout << "             Mismatch PSNR: "
            << psnr_resilience_mismatch << "\n";
  EXPECT_GT(psnr_resilience_mismatch, 20.0);
282
#endif
283 284
}

285 286 287 288 289
// Check for successful decoding and no encoder/decoder mismatch
// if we lose (i.e., drop before decoding) the enhancement layer frames for a
// two layer temporal pattern. The base layer does not predict from the top
// layer, so successful decoding is expected.
TEST_P(ErrorResilienceTestLarge, 2LayersDropEnhancement) {
290
  // This test doesn't run if SVC is not supported.
clang-format's avatar
clang-format committed
291
  if (!svc_support_) return;
292

Adrian Grange's avatar
Adrian Grange committed
293
  const aom_rational timebase = { 33333333, 1000000000 };
294 295 296 297
  cfg_.g_timebase = timebase;
  cfg_.rc_target_bitrate = 500;
  cfg_.g_lag_in_frames = 0;

Adrian Grange's avatar
Adrian Grange committed
298
  cfg_.rc_end_usage = AOM_CBR;
299 300 301 302 303 304 305 306 307
  // 2 Temporal layers, no spatial layers, CBR mode.
  cfg_.ss_number_layers = 1;
  cfg_.ts_number_layers = 2;
  cfg_.ts_rate_decimator[0] = 2;
  cfg_.ts_rate_decimator[1] = 1;
  cfg_.ts_periodicity = 2;
  cfg_.ts_target_bitrate[0] = 60 * cfg_.rc_target_bitrate / 100;
  cfg_.ts_target_bitrate[1] = cfg_.rc_target_bitrate;

Adrian Grange's avatar
Adrian Grange committed
308
  init_flags_ = AOM_CODEC_USE_PSNR;
309

Yaowu Xu's avatar
Yaowu Xu committed
310
  libaom_test::I420VideoSource video("hantro_collage_w352h288.yuv", 352, 288,
311 312 313 314
                                     timebase.den, timebase.num, 0, 40);

  // Error resilient mode ON.
  cfg_.g_error_resilient = 1;
Adrian Grange's avatar
Adrian Grange committed
315
  cfg_.kf_mode = AOM_KF_DISABLED;
Marco's avatar
Marco committed
316
  SetPatternSwitch(0);
317 318 319 320

  // The odd frames are the enhancement layer for 2 layer pattern, so set
  // those frames as droppable. Drop the last 7 frames.
  unsigned int num_droppable_frames = 7;
clang-format's avatar
clang-format committed
321
  unsigned int droppable_frame_list[] = { 27, 29, 31, 33, 35, 37, 39 };
322 323 324 325
  SetDroppableFrames(num_droppable_frames, droppable_frame_list);
  SetErrorFrames(num_droppable_frames, droppable_frame_list);
  ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
  // Test that no mismatches have been found
clang-format's avatar
clang-format committed
326 327
  std::cout << "             Mismatch frames: " << GetMismatchFrames() << "\n";
  EXPECT_EQ(GetMismatchFrames(), (unsigned int)0);
328 329

  // Reset previously set of error/droppable frames.
Marco's avatar
Marco committed
330 331 332 333 334 335 336
  Reset();
}

// Check for successful decoding and no encoder/decoder mismatch
// for a two layer temporal pattern, where at some point in the
// sequence, the LAST ref is not used anymore.
TEST_P(ErrorResilienceTestLarge, 2LayersNoRefLast) {
337
  // This test doesn't run if SVC is not supported.
clang-format's avatar
clang-format committed
338
  if (!svc_support_) return;
339

Adrian Grange's avatar
Adrian Grange committed
340
  const aom_rational timebase = { 33333333, 1000000000 };
Marco's avatar
Marco committed
341 342 343 344
  cfg_.g_timebase = timebase;
  cfg_.rc_target_bitrate = 500;
  cfg_.g_lag_in_frames = 0;

Adrian Grange's avatar
Adrian Grange committed
345
  cfg_.rc_end_usage = AOM_CBR;
Marco's avatar
Marco committed
346 347 348 349 350 351 352 353 354
  // 2 Temporal layers, no spatial layers, CBR mode.
  cfg_.ss_number_layers = 1;
  cfg_.ts_number_layers = 2;
  cfg_.ts_rate_decimator[0] = 2;
  cfg_.ts_rate_decimator[1] = 1;
  cfg_.ts_periodicity = 2;
  cfg_.ts_target_bitrate[0] = 60 * cfg_.rc_target_bitrate / 100;
  cfg_.ts_target_bitrate[1] = cfg_.rc_target_bitrate;

Adrian Grange's avatar
Adrian Grange committed
355
  init_flags_ = AOM_CODEC_USE_PSNR;
Marco's avatar
Marco committed
356

Yaowu Xu's avatar
Yaowu Xu committed
357
  libaom_test::I420VideoSource video("hantro_collage_w352h288.yuv", 352, 288,
Marco's avatar
Marco committed
358 359 360 361
                                     timebase.den, timebase.num, 0, 100);

  // Error resilient mode ON.
  cfg_.g_error_resilient = 1;
Adrian Grange's avatar
Adrian Grange committed
362
  cfg_.kf_mode = AOM_KF_DISABLED;
Marco's avatar
Marco committed
363 364 365 366
  SetPatternSwitch(60);

  ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
  // Test that no mismatches have been found
clang-format's avatar
clang-format committed
367 368
  std::cout << "             Mismatch frames: " << GetMismatchFrames() << "\n";
  EXPECT_EQ(GetMismatchFrames(), (unsigned int)0);
Marco's avatar
Marco committed
369 370

  // Reset previously set of error/droppable frames.
371 372 373
  Reset();
}

clang-format's avatar
clang-format committed
374
class ErrorResilienceTestLargeCodecControls
Yaowu Xu's avatar
Yaowu Xu committed
375 376
    : public ::libaom_test::EncoderTest,
      public ::libaom_test::CodecTestWithParam<libaom_test::TestMode> {
Marco's avatar
Marco committed
377 378
 protected:
  ErrorResilienceTestLargeCodecControls()
clang-format's avatar
clang-format committed
379
      : EncoderTest(GET_PARAM(0)), encoding_mode_(GET_PARAM(1)) {
Marco's avatar
Marco committed
380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417
    Reset();
  }

  virtual ~ErrorResilienceTestLargeCodecControls() {}

  void Reset() {
    last_pts_ = 0;
    tot_frame_number_ = 0;
    // For testing up to 3 layers.
    for (int i = 0; i < 3; ++i) {
      bits_total_[i] = 0;
    }
    duration_ = 0.0;
  }

  virtual void SetUp() {
    InitializeConfig();
    SetMode(encoding_mode_);
  }

  //
  // Frame flags and layer id for temporal layers.
  //

  // For two layers, test pattern is:
  //   1     3
  // 0    2     .....
  // For three layers, test pattern is:
  //   1      3    5      7
  //      2           6
  // 0          4            ....
  // LAST is always update on base/layer 0, GOLDEN is updated on layer 1,
  // and ALTREF is updated on top layer for 3 layer pattern.
  int SetFrameFlags(int frame_num, int num_temp_layers) {
    int frame_flags = 0;
    if (num_temp_layers == 2) {
      if (frame_num % 2 == 0) {
        // Layer 0: predict from L and ARF, update L.
clang-format's avatar
clang-format committed
418
        frame_flags =
Adrian Grange's avatar
Adrian Grange committed
419
            AOM_EFLAG_NO_REF_GF | AOM_EFLAG_NO_UPD_GF | AOM_EFLAG_NO_UPD_ARF;
Marco's avatar
Marco committed
420 421
      } else {
        // Layer 1: predict from L, G and ARF, and update G.
Adrian Grange's avatar
Adrian Grange committed
422 423
        frame_flags = AOM_EFLAG_NO_UPD_ARF | AOM_EFLAG_NO_UPD_LAST |
                      AOM_EFLAG_NO_UPD_ENTROPY;
Marco's avatar
Marco committed
424 425 426 427
      }
    } else if (num_temp_layers == 3) {
      if (frame_num % 4 == 0) {
        // Layer 0: predict from L, update L.
Adrian Grange's avatar
Adrian Grange committed
428 429
        frame_flags = AOM_EFLAG_NO_UPD_GF | AOM_EFLAG_NO_UPD_ARF |
                      AOM_EFLAG_NO_REF_GF | AOM_EFLAG_NO_REF_ARF;
Marco's avatar
Marco committed
430 431
      } else if ((frame_num - 2) % 4 == 0) {
        // Layer 1: predict from L, G,  update G.
clang-format's avatar
clang-format committed
432
        frame_flags =
Adrian Grange's avatar
Adrian Grange committed
433
            AOM_EFLAG_NO_UPD_ARF | AOM_EFLAG_NO_UPD_LAST | AOM_EFLAG_NO_REF_ARF;
clang-format's avatar
clang-format committed
434
      } else if ((frame_num - 1) % 2 == 0) {
Marco's avatar
Marco committed
435
        // Layer 2: predict from L, G, ARF; update ARG.
Adrian Grange's avatar
Adrian Grange committed
436
        frame_flags = AOM_EFLAG_NO_UPD_GF | AOM_EFLAG_NO_UPD_LAST;
Marco's avatar
Marco committed
437 438 439 440 441 442 443 444 445 446 447
      }
    }
    return frame_flags;
  }

  int SetLayerId(int frame_num, int num_temp_layers) {
    int layer_id = 0;
    if (num_temp_layers == 2) {
      if (frame_num % 2 == 0) {
        layer_id = 0;
      } else {
clang-format's avatar
clang-format committed
448
        layer_id = 1;
Marco's avatar
Marco committed
449 450 451 452 453 454 455 456 457 458 459 460 461
      }
    } else if (num_temp_layers == 3) {
      if (frame_num % 4 == 0) {
        layer_id = 0;
      } else if ((frame_num - 2) % 4 == 0) {
        layer_id = 1;
      } else if ((frame_num - 1) % 2 == 0) {
        layer_id = 2;
      }
    }
    return layer_id;
  }

Yaowu Xu's avatar
Yaowu Xu committed
462 463
  virtual void PreEncodeFrameHook(libaom_test::VideoSource *video,
                                  libaom_test::Encoder *encoder) {
Marco's avatar
Marco committed
464
    if (cfg_.ts_number_layers > 1) {
clang-format's avatar
clang-format committed
465 466 467
      int layer_id = SetLayerId(video->frame(), cfg_.ts_number_layers);
      int frame_flags = SetFrameFlags(video->frame(), cfg_.ts_number_layers);
      if (video->frame() > 0) {
Adrian Grange's avatar
Adrian Grange committed
468 469
        encoder->Control(AOME_SET_TEMPORAL_LAYER_ID, layer_id);
        encoder->Control(AOME_SET_FRAME_FLAGS, frame_flags);
clang-format's avatar
clang-format committed
470
      }
Adrian Grange's avatar
Adrian Grange committed
471
      const aom_rational_t tb = video->timebase();
clang-format's avatar
clang-format committed
472 473 474
      timebase_ = static_cast<double>(tb.num) / tb.den;
      duration_ = 0;
      return;
Marco's avatar
Marco committed
475 476 477
    }
  }

Adrian Grange's avatar
Adrian Grange committed
478
  virtual void FramePktHook(const aom_codec_cx_pkt_t *pkt) {
Marco's avatar
Marco committed
479
    // Time since last timestamp = duration.
Adrian Grange's avatar
Adrian Grange committed
480
    aom_codec_pts_t duration = pkt->data.frame.pts - last_pts_;
Marco's avatar
Marco committed
481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499
    if (duration > 1) {
      // Update counter for total number of frames (#frames input to encoder).
      // Needed for setting the proper layer_id below.
      tot_frame_number_ += static_cast<int>(duration - 1);
    }
    int layer = SetLayerId(tot_frame_number_, cfg_.ts_number_layers);
    const size_t frame_size_in_bits = pkt->data.frame.sz * 8;
    // Update the total encoded bits. For temporal layers, update the cumulative
    // encoded bits per layer.
    for (int i = layer; i < static_cast<int>(cfg_.ts_number_layers); ++i) {
      bits_total_[i] += frame_size_in_bits;
    }
    // Update the most recent pts.
    last_pts_ = pkt->data.frame.pts;
    ++tot_frame_number_;
  }

  virtual void EndPassHook(void) {
    duration_ = (last_pts_ + 1) * timebase_;
clang-format's avatar
clang-format committed
500
    if (cfg_.ts_number_layers > 1) {
Marco's avatar
Marco committed
501
      for (int layer = 0; layer < static_cast<int>(cfg_.ts_number_layers);
clang-format's avatar
clang-format committed
502
           ++layer) {
Marco's avatar
Marco committed
503 504
        if (bits_total_[layer]) {
          // Effective file datarate:
clang-format's avatar
clang-format committed
505 506
          effective_datarate_[layer] =
              (bits_total_[layer] / 1000.0) / duration_;
Marco's avatar
Marco committed
507 508 509 510 511 512
        }
      }
    }
  }

  double effective_datarate_[3];
clang-format's avatar
clang-format committed
513 514

 private:
Yaowu Xu's avatar
Yaowu Xu committed
515
  libaom_test::TestMode encoding_mode_;
Adrian Grange's avatar
Adrian Grange committed
516
  aom_codec_pts_t last_pts_;
clang-format's avatar
clang-format committed
517 518 519 520 521
  double timebase_;
  int64_t bits_total_[3];
  double duration_;
  int tot_frame_number_;
};
Marco's avatar
Marco committed
522 523 524 525 526 527 528 529 530 531 532 533 534

// Check two codec controls used for:
// (1) for setting temporal layer id, and (2) for settings encoder flags.
// This test invokes those controls for each frame, and verifies encoder/decoder
// mismatch and basic rate control response.
// TODO(marpan): Maybe move this test to datarate_test.cc.
TEST_P(ErrorResilienceTestLargeCodecControls, CodecControl3TemporalLayers) {
  cfg_.rc_buf_initial_sz = 500;
  cfg_.rc_buf_optimal_sz = 500;
  cfg_.rc_buf_sz = 1000;
  cfg_.rc_dropframe_thresh = 1;
  cfg_.rc_min_quantizer = 2;
  cfg_.rc_max_quantizer = 56;
Adrian Grange's avatar
Adrian Grange committed
535
  cfg_.rc_end_usage = AOM_CBR;
Marco's avatar
Marco committed
536 537
  cfg_.rc_dropframe_thresh = 1;
  cfg_.g_lag_in_frames = 0;
Adrian Grange's avatar
Adrian Grange committed
538
  cfg_.kf_mode = AOM_KF_DISABLED;
Marco's avatar
Marco committed
539 540 541 542 543 544 545 546 547 548 549 550 551
  cfg_.g_error_resilient = 1;

  // 3 Temporal layers. Framerate decimation (4, 2, 1).
  cfg_.ts_number_layers = 3;
  cfg_.ts_rate_decimator[0] = 4;
  cfg_.ts_rate_decimator[1] = 2;
  cfg_.ts_rate_decimator[2] = 1;
  cfg_.ts_periodicity = 4;
  cfg_.ts_layer_id[0] = 0;
  cfg_.ts_layer_id[1] = 2;
  cfg_.ts_layer_id[2] = 1;
  cfg_.ts_layer_id[3] = 2;

Yaowu Xu's avatar
Yaowu Xu committed
552
  ::libaom_test::I420VideoSource video("hantro_collage_w352h288.yuv", 352, 288,
Marco's avatar
Marco committed
553 554 555 556 557 558 559 560 561 562 563 564
                                       30, 1, 0, 200);
  for (int i = 200; i <= 800; i += 200) {
    cfg_.rc_target_bitrate = i;
    Reset();
    // 40-20-40 bitrate allocation for 3 temporal layers.
    cfg_.ts_target_bitrate[0] = 40 * cfg_.rc_target_bitrate / 100;
    cfg_.ts_target_bitrate[1] = 60 * cfg_.rc_target_bitrate / 100;
    cfg_.ts_target_bitrate[2] = cfg_.rc_target_bitrate;
    ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
    for (int j = 0; j < static_cast<int>(cfg_.ts_number_layers); ++j) {
      ASSERT_GE(effective_datarate_[j], cfg_.ts_target_bitrate[j] * 0.75)
          << " The datarate for the file is lower than target by too much, "
clang-format's avatar
clang-format committed
565 566
             "for layer: "
          << j;
Marco's avatar
Marco committed
567 568
      ASSERT_LE(effective_datarate_[j], cfg_.ts_target_bitrate[j] * 1.25)
          << " The datarate for the file is greater than target by too much, "
clang-format's avatar
clang-format committed
569 570
             "for layer: "
          << j;
Marco's avatar
Marco committed
571 572 573 574
    }
  }
}

575 576
// SVC-related tests don't run for AV1 since SVC is not supported.
AV1_INSTANTIATE_TEST_CASE(ErrorResilienceTestLarge, ONE_PASS_TEST_MODES,
577
                           ::testing::Values(false));
578
}  // namespace