]> granicus.if.org Git - libvpx/commitdiff
vp9: Allow for disabling loopfilter per spatial layer
authorMarco Paniconi <marpan@google.com>
Thu, 12 Nov 2020 07:11:16 +0000 (23:11 -0800)
committerMarco Paniconi <marpan@google.com>
Thu, 12 Nov 2020 19:31:42 +0000 (11:31 -0800)
For SVC: add parameter to the control SET_SVC_PARAMS to
allow for disabling the loopfilter per spatial layer.
Note this svc setting will override the setting via
VP9E_SET_DISABLE_LOOPFILTER (which should only be used
for non-SVC).

Add unittest to handle both SVC (spatial or temporal layers)
and non-SVC (single layer) case.

Change-Id: I4092f01668bae42aac724a6df5b6f6a604337448

test/svc_end_to_end_test.cc
test/svc_test.cc
vp9/encoder/vp9_svc_layercontext.c
vp9/encoder/vp9_svc_layercontext.h
vp9/vp9_cx_iface.c
vpx/vpx_encoder.h

index b1ab0d7d95261c0d6ae8652bc2a88a4c7171e737..518824d03fc16347ac032815f1492e4cee9abcdf 100644 (file)
@@ -494,7 +494,31 @@ class LoopfilterOnePassCbrSvc : public OnePassCbrSvc,
   virtual void PreEncodeFrameHook(::libvpx_test::VideoSource *video,
                                   ::libvpx_test::Encoder *encoder) {
     PreEncodeFrameHookSetup(video, encoder);
-    encoder->Control(VP9E_SET_DISABLE_LOOPFILTER, loopfilter_off_);
+    if (number_temporal_layers_ > 1 || number_spatial_layers_ > 1) {
+      // Consider 3 cases:
+      if (loopfilter_off_ == 0) {
+        // loopfilter is on for all spatial layers on every superrframe.
+        for (int i = 0; i < VPX_SS_MAX_LAYERS; ++i) {
+          svc_params_.loopfilter_ctrl[i] = 0;
+        }
+      } else if (loopfilter_off_ == 1) {
+        // loopfilter is off for non-reference frames for all spatial layers.
+        for (int i = 0; i < VPX_SS_MAX_LAYERS; ++i) {
+          svc_params_.loopfilter_ctrl[i] = 1;
+        }
+      } else {
+        // loopfilter is off for all SL0 frames, and off only for non-reference
+        // frames for SL > 0.
+        svc_params_.loopfilter_ctrl[0] = 2;
+        for (int i = 1; i < VPX_SS_MAX_LAYERS; ++i) {
+          svc_params_.loopfilter_ctrl[i] = 1;
+        }
+      }
+      encoder->Control(VP9E_SET_SVC_PARAMETERS, &svc_params_);
+    } else if (number_temporal_layers_ == 1 && number_spatial_layers_ == 1) {
+      // For non-SVC mode use the single layer control.
+      encoder->Control(VP9E_SET_DISABLE_LOOPFILTER, loopfilter_off_);
+    }
   }
 
   virtual void FramePktHook(const vpx_codec_cx_pkt_t *pkt) {
@@ -524,6 +548,35 @@ class LoopfilterOnePassCbrSvc : public OnePassCbrSvc,
   int num_nonref_frames_;
 };
 
+TEST_P(LoopfilterOnePassCbrSvc, OnePassCbrSvc1SL1TLLoopfilterOff) {
+  SetSvcConfig(1, 1);
+  cfg_.rc_buf_initial_sz = 500;
+  cfg_.rc_buf_optimal_sz = 500;
+  cfg_.rc_buf_sz = 1000;
+  cfg_.rc_min_quantizer = 0;
+  cfg_.rc_max_quantizer = 63;
+  cfg_.g_threads = 1;
+  cfg_.rc_dropframe_thresh = 0;
+  cfg_.rc_target_bitrate = 800;
+  cfg_.kf_max_dist = 9999;
+  cfg_.rc_end_usage = VPX_CBR;
+  cfg_.g_lag_in_frames = 0;
+  cfg_.g_error_resilient = 1;
+  cfg_.ts_rate_decimator[0] = 1;
+  cfg_.temporal_layering_mode = 0;
+  ::libvpx_test::I420VideoSource video("niklas_640_480_30.yuv", 640, 480, 30, 1,
+                                       0, 400);
+  cfg_.rc_target_bitrate = 600;
+  AssignLayerBitrates();
+  ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
+#if CONFIG_VP9_DECODER
+  if (loopfilter_off_ == 0)
+    EXPECT_EQ(GetNonRefFrames(), GetMismatchFrames());
+  else
+    EXPECT_EQ(GetMismatchFrames(), 0);
+#endif
+}
+
 TEST_P(LoopfilterOnePassCbrSvc, OnePassCbrSvc1SL3TLLoopfilterOff) {
   SetSvcConfig(1, 3);
   cfg_.rc_buf_initial_sz = 500;
@@ -542,7 +595,37 @@ TEST_P(LoopfilterOnePassCbrSvc, OnePassCbrSvc1SL3TLLoopfilterOff) {
   cfg_.ts_rate_decimator[1] = 2;
   cfg_.ts_rate_decimator[2] = 1;
   cfg_.temporal_layering_mode = 3;
+  ::libvpx_test::I420VideoSource video("niklas_640_480_30.yuv", 640, 480, 30, 1,
+                                       0, 400);
+  cfg_.rc_target_bitrate = 600;
+  AssignLayerBitrates();
+  ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
+#if CONFIG_VP9_DECODER
+  if (loopfilter_off_ == 0)
+    EXPECT_EQ(GetNonRefFrames(), GetMismatchFrames());
+  else
+    EXPECT_EQ(GetMismatchFrames(), 0);
+#endif
+}
 
+TEST_P(LoopfilterOnePassCbrSvc, OnePassCbrSvc3SL3TLLoopfilterOff) {
+  SetSvcConfig(3, 3);
+  cfg_.rc_buf_initial_sz = 500;
+  cfg_.rc_buf_optimal_sz = 500;
+  cfg_.rc_buf_sz = 1000;
+  cfg_.rc_min_quantizer = 0;
+  cfg_.rc_max_quantizer = 63;
+  cfg_.g_threads = 1;
+  cfg_.rc_dropframe_thresh = 0;
+  cfg_.rc_target_bitrate = 800;
+  cfg_.kf_max_dist = 9999;
+  cfg_.rc_end_usage = VPX_CBR;
+  cfg_.g_lag_in_frames = 0;
+  cfg_.g_error_resilient = 1;
+  cfg_.ts_rate_decimator[0] = 4;
+  cfg_.ts_rate_decimator[1] = 2;
+  cfg_.ts_rate_decimator[2] = 1;
+  cfg_.temporal_layering_mode = 3;
   ::libvpx_test::I420VideoSource video("niklas_640_480_30.yuv", 640, 480, 30, 1,
                                        0, 400);
   cfg_.rc_target_bitrate = 600;
index 4798c7718323128c55409d4294d044511d200f64..cbc0abe032da3675e74478f7a933d7ec3ab8053e 100644 (file)
@@ -43,13 +43,14 @@ void OnePassCbrSvc::PreEncodeFrameHookSetup(::libvpx_test::VideoSource *video,
       svc_params_.max_quantizers[i] = 63;
       svc_params_.min_quantizers[i] = 0;
     }
-    svc_params_.speed_per_layer[0] = base_speed_setting_;
-    for (int i = 1; i < VPX_SS_MAX_LAYERS; ++i) {
-      svc_params_.speed_per_layer[i] = speed_setting_;
+    if (number_temporal_layers_ > 1 || number_spatial_layers_ > 1) {
+      svc_params_.speed_per_layer[0] = base_speed_setting_;
+      for (int i = 1; i < VPX_SS_MAX_LAYERS; ++i) {
+        svc_params_.speed_per_layer[i] = speed_setting_;
+      }
+      encoder->Control(VP9E_SET_SVC, 1);
+      encoder->Control(VP9E_SET_SVC_PARAMETERS, &svc_params_);
     }
-
-    encoder->Control(VP9E_SET_SVC, 1);
-    encoder->Control(VP9E_SET_SVC_PARAMETERS, &svc_params_);
     encoder->Control(VP8E_SET_CPUUSED, speed_setting_);
     encoder->Control(VP9E_SET_AQ_MODE, 3);
     encoder->Control(VP8E_SET_MAX_INTRA_BITRATE_PCT, 300);
index d85b3632cd59a72d24c6793da0c5e56ff3f39e21..9c75d7726355c245e8e95ff5473e6b8d7a151144 100644 (file)
@@ -357,6 +357,8 @@ void vp9_restore_layer_context(VP9_COMP *const cpi) {
   if (is_one_pass_cbr_svc(cpi) && lc->speed > 0) {
     cpi->oxcf.speed = lc->speed;
   }
+  if (lc->loopfilter_ctrl >= 0 || lc->loopfilter_ctrl < 3)
+    cpi->loopfilter_ctrl = lc->loopfilter_ctrl;
   // Reset the frames_since_key and frames_to_key counters to their values
   // before the layer restore. Keep these defined for the stream (not layer).
   if (cpi->svc.number_temporal_layers > 1 ||
index e7d9712aae25836e0ab2cd7232b2688b07b68408..b12e7e01a759ed79c24433612fba2d2701f96dd8 100644 (file)
@@ -71,6 +71,7 @@ typedef struct {
   int actual_num_seg2_blocks;
   int counter_encode_maxq_scene_change;
   uint8_t speed;
+  int loopfilter_ctrl;
 } LAYER_CONTEXT;
 
 typedef struct SVC {
index aa13fc9cf1f102c42cbb2fdff784a89a7d64b103..0ccb6750b6b1cf8d7fcee942bcc31f4ac2528da4 100644 (file)
@@ -1573,6 +1573,7 @@ static vpx_codec_err_t ctrl_set_svc_parameters(vpx_codec_alg_priv_t *ctx,
       lc->scaling_factor_num = params->scaling_factor_num[sl];
       lc->scaling_factor_den = params->scaling_factor_den[sl];
       lc->speed = params->speed_per_layer[sl];
+      lc->loopfilter_ctrl = params->loopfilter_ctrl[sl];
     }
   }
 
index c84d40f7f7f1ef3128a30aae98d38ab3eb07d80b..39b2aef6259dbec3e5bbc1206cd0d41e454d80cd 100644 (file)
@@ -705,6 +705,7 @@ typedef struct vpx_svc_parameters {
   int scaling_factor_den[VPX_MAX_LAYERS]; /**< Scaling factor-denominator */
   int speed_per_layer[VPX_MAX_LAYERS];    /**< Speed setting for each sl */
   int temporal_layering_mode;             /**< Temporal layering mode */
+  int loopfilter_ctrl[VPX_MAX_LAYERS];    /**< Loopfilter ctrl for each sl */
 } vpx_svc_extra_cfg_t;
 
 /*!\brief Initialize an encoder instance