2 * Copyright (c) 2013 The WebM project authors. All Rights Reserved.
4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
11 #include "third_party/googletest/src/include/gtest/gtest.h"
12 #include "test/codec_factory.h"
13 #include "test/encode_test_driver.h"
14 #include "test/i420_video_source.h"
15 #include "test/util.h"
19 const int kMaxErrorFrames = 12;
20 const int kMaxDroppableFrames = 12;
22 class ErrorResilienceTestLarge : public ::libvpx_test::EncoderTest,
23 public ::libvpx_test::CodecTestWithParam<libvpx_test::TestMode> {
25 ErrorResilienceTestLarge()
26 : EncoderTest(GET_PARAM(0)),
31 encoding_mode_(GET_PARAM(1)) {
35 virtual ~ErrorResilienceTestLarge() {}
39 droppable_nframes_ = 0;
43 virtual void SetUp() {
45 SetMode(encoding_mode_);
48 virtual void BeginPassHook(unsigned int /*pass*/) {
52 mismatch_nframes_ = 0;
55 virtual void PSNRPktHook(const vpx_codec_cx_pkt_t *pkt) {
56 psnr_ += pkt->data.psnr.psnr[0];
61 // Frame flags and layer id for temporal layers.
62 // For two layers, test pattern is:
65 // LAST is updated on base/layer 0, GOLDEN updated on layer 1.
66 // Non-zero pattern_switch parameter means pattern will switch to
67 // not using LAST for frame_num >= pattern_switch.
68 int SetFrameFlags(int frame_num,
72 if (num_temp_layers == 2) {
73 if (frame_num % 2 == 0) {
74 if (frame_num < pattern_switch || pattern_switch == 0) {
75 // Layer 0: predict from LAST and ARF, update LAST.
76 frame_flags = VP8_EFLAG_NO_REF_GF |
80 // Layer 0: predict from GF and ARF, update GF.
81 frame_flags = VP8_EFLAG_NO_REF_LAST |
82 VP8_EFLAG_NO_UPD_LAST |
86 if (frame_num < pattern_switch || pattern_switch == 0) {
87 // Layer 1: predict from L, GF, and ARF, update GF.
88 frame_flags = VP8_EFLAG_NO_UPD_ARF |
89 VP8_EFLAG_NO_UPD_LAST;
91 // Layer 1: predict from GF and ARF, update GF.
92 frame_flags = VP8_EFLAG_NO_REF_LAST |
93 VP8_EFLAG_NO_UPD_LAST |
101 virtual void PreEncodeFrameHook(libvpx_test::VideoSource *video,
102 ::libvpx_test::Encoder *encoder) {
103 frame_flags_ &= ~(VP8_EFLAG_NO_UPD_LAST |
104 VP8_EFLAG_NO_UPD_GF |
105 VP8_EFLAG_NO_UPD_ARF);
106 // For temporal layer case.
107 if (cfg_.ts_number_layers > 1) {
108 frame_flags_ = SetFrameFlags(video->frame(),
109 cfg_.ts_number_layers,
111 for (unsigned int i = 0; i < droppable_nframes_; ++i) {
112 if (droppable_frames_[i] == video->frame()) {
113 std::cout << "Encoding droppable frame: "
114 << droppable_frames_[i] << "\n";
118 if (droppable_nframes_ > 0 &&
119 (cfg_.g_pass == VPX_RC_LAST_PASS || cfg_.g_pass == VPX_RC_ONE_PASS)) {
120 for (unsigned int i = 0; i < droppable_nframes_; ++i) {
121 if (droppable_frames_[i] == video->frame()) {
122 std::cout << "Encoding droppable frame: "
123 << droppable_frames_[i] << "\n";
124 frame_flags_ |= (VP8_EFLAG_NO_UPD_LAST |
125 VP8_EFLAG_NO_UPD_GF |
126 VP8_EFLAG_NO_UPD_ARF);
134 double GetAveragePsnr() const {
136 return psnr_ / nframes_;
140 double GetAverageMismatchPsnr() const {
141 if (mismatch_nframes_)
142 return mismatch_psnr_ / mismatch_nframes_;
146 virtual bool DoDecode() const {
147 if (error_nframes_ > 0 &&
148 (cfg_.g_pass == VPX_RC_LAST_PASS || cfg_.g_pass == VPX_RC_ONE_PASS)) {
149 for (unsigned int i = 0; i < error_nframes_; ++i) {
150 if (error_frames_[i] == nframes_ - 1) {
151 std::cout << " Skipping decoding frame: "
152 << error_frames_[i] << "\n";
160 virtual void MismatchHook(const vpx_image_t *img1,
161 const vpx_image_t *img2) {
162 double mismatch_psnr = compute_psnr(img1, img2);
163 mismatch_psnr_ += mismatch_psnr;
165 // std::cout << "Mismatch frame psnr: " << mismatch_psnr << "\n";
168 void SetErrorFrames(int num, unsigned int *list) {
169 if (num > kMaxErrorFrames)
170 num = kMaxErrorFrames;
173 error_nframes_ = num;
174 for (unsigned int i = 0; i < error_nframes_; ++i)
175 error_frames_[i] = list[i];
178 void SetDroppableFrames(int num, unsigned int *list) {
179 if (num > kMaxDroppableFrames)
180 num = kMaxDroppableFrames;
183 droppable_nframes_ = num;
184 for (unsigned int i = 0; i < droppable_nframes_; ++i)
185 droppable_frames_[i] = list[i];
188 unsigned int GetMismatchFrames() {
189 return mismatch_nframes_;
192 void SetPatternSwitch(int frame_switch) {
193 pattern_switch_ = frame_switch;
198 unsigned int nframes_;
199 unsigned int error_nframes_;
200 unsigned int droppable_nframes_;
201 unsigned int pattern_switch_;
202 double mismatch_psnr_;
203 unsigned int mismatch_nframes_;
204 unsigned int error_frames_[kMaxErrorFrames];
205 unsigned int droppable_frames_[kMaxDroppableFrames];
206 libvpx_test::TestMode encoding_mode_;
209 TEST_P(ErrorResilienceTestLarge, OnVersusOff) {
210 const vpx_rational timebase = { 33333333, 1000000000 };
211 cfg_.g_timebase = timebase;
212 cfg_.rc_target_bitrate = 2000;
213 cfg_.g_lag_in_frames = 10;
215 init_flags_ = VPX_CODEC_USE_PSNR;
217 libvpx_test::I420VideoSource video("hantro_collage_w352h288.yuv", 352, 288,
218 timebase.den, timebase.num, 0, 30);
220 // Error resilient mode OFF.
221 cfg_.g_error_resilient = 0;
222 ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
223 const double psnr_resilience_off = GetAveragePsnr();
224 EXPECT_GT(psnr_resilience_off, 25.0);
226 // Error resilient mode ON.
227 cfg_.g_error_resilient = 1;
228 ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
229 const double psnr_resilience_on = GetAveragePsnr();
230 EXPECT_GT(psnr_resilience_on, 25.0);
232 // Test that turning on error resilient mode hurts by 10% at most.
233 if (psnr_resilience_off > 0.0) {
234 const double psnr_ratio = psnr_resilience_on / psnr_resilience_off;
235 EXPECT_GE(psnr_ratio, 0.9);
236 EXPECT_LE(psnr_ratio, 1.1);
240 // Check for successful decoding and no encoder/decoder mismatch
241 // if we lose (i.e., drop before decoding) a set of droppable
242 // frames (i.e., frames that don't update any reference buffers).
243 // Check both isolated and consecutive loss.
244 TEST_P(ErrorResilienceTestLarge, DropFramesWithoutRecovery) {
245 const vpx_rational timebase = { 33333333, 1000000000 };
246 cfg_.g_timebase = timebase;
247 cfg_.rc_target_bitrate = 500;
248 // FIXME(debargha): Fix this to work for any lag.
249 // Currently this test only works for lag = 0
250 cfg_.g_lag_in_frames = 0;
252 init_flags_ = VPX_CODEC_USE_PSNR;
254 libvpx_test::I420VideoSource video("hantro_collage_w352h288.yuv", 352, 288,
255 timebase.den, timebase.num, 0, 40);
257 // Error resilient mode ON.
258 cfg_.g_error_resilient = 1;
259 cfg_.kf_mode = VPX_KF_DISABLED;
261 // Set an arbitrary set of error frames same as droppable frames.
262 // In addition to isolated loss/drop, add a long consecutive series
263 // (of size 9) of dropped frames.
264 unsigned int num_droppable_frames = 11;
265 unsigned int droppable_frame_list[] = {5, 16, 22, 23, 24, 25, 26, 27, 28,
267 SetDroppableFrames(num_droppable_frames, droppable_frame_list);
268 SetErrorFrames(num_droppable_frames, droppable_frame_list);
269 ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
270 // Test that no mismatches have been found
271 std::cout << " Mismatch frames: "
272 << GetMismatchFrames() << "\n";
273 EXPECT_EQ(GetMismatchFrames(), (unsigned int) 0);
275 // Reset previously set of error/droppable frames.
279 // TODO(jkoleszar): This test is disabled for the time being as too
280 // sensitive. It's not clear how to set a reasonable threshold for
283 // Now set an arbitrary set of error frames that are non-droppable
284 unsigned int num_error_frames = 3;
285 unsigned int error_frame_list[] = {3, 10, 20};
286 SetErrorFrames(num_error_frames, error_frame_list);
287 ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
289 // Test that dropping an arbitrary set of inter frames does not hurt too much
290 // Note the Average Mismatch PSNR is the average of the PSNR between
291 // decoded frame and encoder's version of the same frame for all frames
293 const double psnr_resilience_mismatch = GetAverageMismatchPsnr();
294 std::cout << " Mismatch PSNR: "
295 << psnr_resilience_mismatch << "\n";
296 EXPECT_GT(psnr_resilience_mismatch, 20.0);
300 // Check for successful decoding and no encoder/decoder mismatch
301 // if we lose (i.e., drop before decoding) the enhancement layer frames for a
302 // two layer temporal pattern. The base layer does not predict from the top
303 // layer, so successful decoding is expected.
304 TEST_P(ErrorResilienceTestLarge, 2LayersDropEnhancement) {
305 const vpx_rational timebase = { 33333333, 1000000000 };
306 cfg_.g_timebase = timebase;
307 cfg_.rc_target_bitrate = 500;
308 cfg_.g_lag_in_frames = 0;
310 cfg_.rc_end_usage = VPX_CBR;
311 // 2 Temporal layers, no spatial layers, CBR mode.
312 cfg_.ss_number_layers = 1;
313 cfg_.ts_number_layers = 2;
314 cfg_.ts_rate_decimator[0] = 2;
315 cfg_.ts_rate_decimator[1] = 1;
316 cfg_.ts_periodicity = 2;
317 cfg_.ts_target_bitrate[0] = 60 * cfg_.rc_target_bitrate / 100;
318 cfg_.ts_target_bitrate[1] = cfg_.rc_target_bitrate;
320 init_flags_ = VPX_CODEC_USE_PSNR;
322 libvpx_test::I420VideoSource video("hantro_collage_w352h288.yuv", 352, 288,
323 timebase.den, timebase.num, 0, 40);
325 // Error resilient mode ON.
326 cfg_.g_error_resilient = 1;
327 cfg_.kf_mode = VPX_KF_DISABLED;
330 // The odd frames are the enhancement layer for 2 layer pattern, so set
331 // those frames as droppable. Drop the last 7 frames.
332 unsigned int num_droppable_frames = 7;
333 unsigned int droppable_frame_list[] = {27, 29, 31, 33, 35, 37, 39};
334 SetDroppableFrames(num_droppable_frames, droppable_frame_list);
335 SetErrorFrames(num_droppable_frames, droppable_frame_list);
336 ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
337 // Test that no mismatches have been found
338 std::cout << " Mismatch frames: "
339 << GetMismatchFrames() << "\n";
340 EXPECT_EQ(GetMismatchFrames(), (unsigned int) 0);
342 // Reset previously set of error/droppable frames.
346 // Check for successful decoding and no encoder/decoder mismatch
347 // for a two layer temporal pattern, where at some point in the
348 // sequence, the LAST ref is not used anymore.
349 TEST_P(ErrorResilienceTestLarge, 2LayersNoRefLast) {
350 const vpx_rational timebase = { 33333333, 1000000000 };
351 cfg_.g_timebase = timebase;
352 cfg_.rc_target_bitrate = 500;
353 cfg_.g_lag_in_frames = 0;
355 cfg_.rc_end_usage = VPX_CBR;
356 // 2 Temporal layers, no spatial layers, CBR mode.
357 cfg_.ss_number_layers = 1;
358 cfg_.ts_number_layers = 2;
359 cfg_.ts_rate_decimator[0] = 2;
360 cfg_.ts_rate_decimator[1] = 1;
361 cfg_.ts_periodicity = 2;
362 cfg_.ts_target_bitrate[0] = 60 * cfg_.rc_target_bitrate / 100;
363 cfg_.ts_target_bitrate[1] = cfg_.rc_target_bitrate;
365 init_flags_ = VPX_CODEC_USE_PSNR;
367 libvpx_test::I420VideoSource video("hantro_collage_w352h288.yuv", 352, 288,
368 timebase.den, timebase.num, 0, 100);
370 // Error resilient mode ON.
371 cfg_.g_error_resilient = 1;
372 cfg_.kf_mode = VPX_KF_DISABLED;
373 SetPatternSwitch(60);
375 ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
376 // Test that no mismatches have been found
377 std::cout << " Mismatch frames: "
378 << GetMismatchFrames() << "\n";
379 EXPECT_EQ(GetMismatchFrames(), (unsigned int) 0);
381 // Reset previously set of error/droppable frames.
385 class ErrorResilienceTestLargeCodecControls : public ::libvpx_test::EncoderTest,
386 public ::libvpx_test::CodecTestWithParam<libvpx_test::TestMode> {
388 ErrorResilienceTestLargeCodecControls()
389 : EncoderTest(GET_PARAM(0)),
390 encoding_mode_(GET_PARAM(1)) {
394 virtual ~ErrorResilienceTestLargeCodecControls() {}
398 tot_frame_number_ = 0;
399 // For testing up to 3 layers.
400 for (int i = 0; i < 3; ++i) {
406 virtual void SetUp() {
408 SetMode(encoding_mode_);
412 // Frame flags and layer id for temporal layers.
415 // For two layers, test pattern is:
418 // For three layers, test pattern is:
422 // LAST is always update on base/layer 0, GOLDEN is updated on layer 1,
423 // and ALTREF is updated on top layer for 3 layer pattern.
424 int SetFrameFlags(int frame_num, int num_temp_layers) {
426 if (num_temp_layers == 2) {
427 if (frame_num % 2 == 0) {
428 // Layer 0: predict from L and ARF, update L.
429 frame_flags = VP8_EFLAG_NO_REF_GF | VP8_EFLAG_NO_UPD_GF |
430 VP8_EFLAG_NO_UPD_ARF;
432 // Layer 1: predict from L, G and ARF, and update G.
433 frame_flags = VP8_EFLAG_NO_UPD_ARF | VP8_EFLAG_NO_UPD_LAST |
434 VP8_EFLAG_NO_UPD_ENTROPY;
436 } else if (num_temp_layers == 3) {
437 if (frame_num % 4 == 0) {
438 // Layer 0: predict from L, update L.
439 frame_flags = VP8_EFLAG_NO_UPD_GF | VP8_EFLAG_NO_UPD_ARF |
440 VP8_EFLAG_NO_REF_GF | VP8_EFLAG_NO_REF_ARF;
441 } else if ((frame_num - 2) % 4 == 0) {
442 // Layer 1: predict from L, G, update G.
443 frame_flags = VP8_EFLAG_NO_UPD_ARF | VP8_EFLAG_NO_UPD_LAST |
444 VP8_EFLAG_NO_REF_ARF;
445 } else if ((frame_num - 1) % 2 == 0) {
446 // Layer 2: predict from L, G, ARF; update ARG.
447 frame_flags = VP8_EFLAG_NO_UPD_GF | VP8_EFLAG_NO_UPD_LAST;
453 int SetLayerId(int frame_num, int num_temp_layers) {
455 if (num_temp_layers == 2) {
456 if (frame_num % 2 == 0) {
461 } else if (num_temp_layers == 3) {
462 if (frame_num % 4 == 0) {
464 } else if ((frame_num - 2) % 4 == 0) {
466 } else if ((frame_num - 1) % 2 == 0) {
473 virtual void PreEncodeFrameHook(libvpx_test::VideoSource *video,
474 libvpx_test::Encoder *encoder) {
475 if (cfg_.ts_number_layers > 1) {
476 int layer_id = SetLayerId(video->frame(), cfg_.ts_number_layers);
477 int frame_flags = SetFrameFlags(video->frame(), cfg_.ts_number_layers);
478 if (video->frame() > 0) {
479 encoder->Control(VP8E_SET_TEMPORAL_LAYER_ID, layer_id);
480 encoder->Control(VP8E_SET_FRAME_FLAGS, frame_flags);
482 const vpx_rational_t tb = video->timebase();
483 timebase_ = static_cast<double>(tb.num) / tb.den;
489 virtual void FramePktHook(const vpx_codec_cx_pkt_t *pkt) {
490 // Time since last timestamp = duration.
491 vpx_codec_pts_t duration = pkt->data.frame.pts - last_pts_;
493 // Update counter for total number of frames (#frames input to encoder).
494 // Needed for setting the proper layer_id below.
495 tot_frame_number_ += static_cast<int>(duration - 1);
497 int layer = SetLayerId(tot_frame_number_, cfg_.ts_number_layers);
498 const size_t frame_size_in_bits = pkt->data.frame.sz * 8;
499 // Update the total encoded bits. For temporal layers, update the cumulative
500 // encoded bits per layer.
501 for (int i = layer; i < static_cast<int>(cfg_.ts_number_layers); ++i) {
502 bits_total_[i] += frame_size_in_bits;
504 // Update the most recent pts.
505 last_pts_ = pkt->data.frame.pts;
509 virtual void EndPassHook(void) {
510 duration_ = (last_pts_ + 1) * timebase_;
511 if (cfg_.ts_number_layers > 1) {
512 for (int layer = 0; layer < static_cast<int>(cfg_.ts_number_layers);
514 if (bits_total_[layer]) {
515 // Effective file datarate:
516 effective_datarate_[layer] = (bits_total_[layer] / 1000.0) / duration_;
522 double effective_datarate_[3];
524 libvpx_test::TestMode encoding_mode_;
525 vpx_codec_pts_t last_pts_;
527 int64_t bits_total_[3];
529 int tot_frame_number_;
532 // Check two codec controls used for:
533 // (1) for setting temporal layer id, and (2) for settings encoder flags.
534 // This test invokes those controls for each frame, and verifies encoder/decoder
535 // mismatch and basic rate control response.
536 // TODO(marpan): Maybe move this test to datarate_test.cc.
537 TEST_P(ErrorResilienceTestLargeCodecControls, CodecControl3TemporalLayers) {
538 cfg_.rc_buf_initial_sz = 500;
539 cfg_.rc_buf_optimal_sz = 500;
540 cfg_.rc_buf_sz = 1000;
541 cfg_.rc_dropframe_thresh = 1;
542 cfg_.rc_min_quantizer = 2;
543 cfg_.rc_max_quantizer = 56;
544 cfg_.rc_end_usage = VPX_CBR;
545 cfg_.rc_dropframe_thresh = 1;
546 cfg_.g_lag_in_frames = 0;
547 cfg_.kf_mode = VPX_KF_DISABLED;
548 cfg_.g_error_resilient = 1;
550 // 3 Temporal layers. Framerate decimation (4, 2, 1).
551 cfg_.ts_number_layers = 3;
552 cfg_.ts_rate_decimator[0] = 4;
553 cfg_.ts_rate_decimator[1] = 2;
554 cfg_.ts_rate_decimator[2] = 1;
555 cfg_.ts_periodicity = 4;
556 cfg_.ts_layer_id[0] = 0;
557 cfg_.ts_layer_id[1] = 2;
558 cfg_.ts_layer_id[2] = 1;
559 cfg_.ts_layer_id[3] = 2;
561 ::libvpx_test::I420VideoSource video("hantro_collage_w352h288.yuv", 352, 288,
563 for (int i = 200; i <= 800; i += 200) {
564 cfg_.rc_target_bitrate = i;
566 // 40-20-40 bitrate allocation for 3 temporal layers.
567 cfg_.ts_target_bitrate[0] = 40 * cfg_.rc_target_bitrate / 100;
568 cfg_.ts_target_bitrate[1] = 60 * cfg_.rc_target_bitrate / 100;
569 cfg_.ts_target_bitrate[2] = cfg_.rc_target_bitrate;
570 ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
571 for (int j = 0; j < static_cast<int>(cfg_.ts_number_layers); ++j) {
572 ASSERT_GE(effective_datarate_[j], cfg_.ts_target_bitrate[j] * 0.75)
573 << " The datarate for the file is lower than target by too much, "
575 ASSERT_LE(effective_datarate_[j], cfg_.ts_target_bitrate[j] * 1.25)
576 << " The datarate for the file is greater than target by too much, "
582 VP8_INSTANTIATE_TEST_CASE(ErrorResilienceTestLarge, ONE_PASS_TEST_MODES);
583 VP8_INSTANTIATE_TEST_CASE(ErrorResilienceTestLargeCodecControls,
584 ONE_PASS_TEST_MODES);
585 VP9_INSTANTIATE_TEST_CASE(ErrorResilienceTestLarge, ONE_PASS_TEST_MODES);