shithub: libvpx

Download patch

ref: f8639b1554c190333f2a16f3f5d93854d7479df1
parent: 1633786bfb5a3566d55ccdf827dd7999b2c2fab4
author: Marco <marpan@google.com>
date: Tue Jan 9 11:35:29 EST 2018

vp9: Skip encoding of enhancement layers on the fly.

For SVC: if an enhancement layer (spatial_layer > 0)
has 0 bandwidth, skip/drop the encoding of the layer.
This allows the application to dynamically disable
higher layers for SVC.

Add flag to signal the skip encoding, this is needed
to modify the packing of the superframe when the top
layer is skipped/dropped.

Also moved some updates (current_video_frame counter and
the last_avg_frame_bandwidth) to the postencode_update_drop_frame().

Added datarate unittest for dynamically going from 3 to 2
and then back to 3 spatial layers.

Change-Id: Idaccdb4aca25ba1d822ed1b4219f94e2e8640d43

--- a/test/datarate_test.cc
+++ b/test/datarate_test.cc
@@ -1227,6 +1227,7 @@
     memset(bits_in_buffer_model_, 0, sizeof(bits_in_buffer_model_));
     memset(bits_total_, 0, sizeof(bits_total_));
     memset(layer_target_avg_bandwidth_, 0, sizeof(layer_target_avg_bandwidth_));
+    dynamic_drop_layer_ = false;
   }
   virtual void BeginPassHook(unsigned int /*pass*/) {}
   virtual void PreEncodeFrameHook(::libvpx_test::VideoSource *video,
@@ -1253,6 +1254,22 @@
       encoder->Control(VP8E_SET_STATIC_THRESHOLD, 1);
       encoder->Control(VP9E_SET_TUNE_CONTENT, tune_content_);
     }
+
+    if (dynamic_drop_layer_) {
+      if (video->frame() == 100) {
+        // Change layer bitrates to set top layer to 0. This will trigger skip
+        // encoding/dropping of top spatial layer.
+        cfg_.rc_target_bitrate -= cfg_.layer_target_bitrate[2];
+        cfg_.layer_target_bitrate[2] = 0;
+        encoder->Config(&cfg_);
+      } else if (video->frame() == 300) {
+        // Change layer bitrate on top layer to non-zero to start encoding it
+        // again.
+        cfg_.layer_target_bitrate[2] = 500;
+        cfg_.rc_target_bitrate += cfg_.layer_target_bitrate[2];
+        encoder->Config(&cfg_);
+      }
+    }
     const vpx_rational_t tb = video->timebase();
     timebase_ = static_cast<double>(tb.num) / tb.den;
     duration_ = 0;
@@ -1317,7 +1334,7 @@
         (pkt->data.frame.flags & VPX_FRAME_IS_KEY) ? true : false;
     parse_superframe_index(static_cast<const uint8_t *>(pkt->data.frame.buf),
                            pkt->data.frame.sz, sizes, &count);
-    ASSERT_EQ(count, number_spatial_layers_);
+    if (!dynamic_drop_layer_) ASSERT_EQ(count, number_spatial_layers_);
     for (int sl = 0; sl < number_spatial_layers_; ++sl) {
       sizes[sl] = sizes[sl] << 3;
       // Update the total encoded bits per layer.
@@ -1375,6 +1392,7 @@
   int number_spatial_layers_;
   int number_temporal_layers_;
   int layer_target_avg_bandwidth_[VPX_MAX_LAYERS];
+  bool dynamic_drop_layer_;
 };
 static void assign_layer_bitrates(vpx_codec_enc_cfg_t *const enc_cfg,
                                   const vpx_svc_extra_cfg_t *svc_params,
@@ -1719,6 +1737,49 @@
   // mismatched frames.
   EXPECT_EQ(static_cast<unsigned int>(200), GetMismatchFrames());
 #endif
+}
+
+// Check basic rate targeting for 1 pass CBR SVC with 3 spatial layers and on
+// the fly switching to 2 spatial layers and then back to 3. This switch is done
+// by setting top spatial layer bitrate to 0, and then back to non-zero, during
+// the sequence.
+TEST_P(DatarateOnePassCbrSvc, OnePassCbrSvc3SL_to_2SL_dynamic) {
+  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_.rc_end_usage = VPX_CBR;
+  cfg_.g_lag_in_frames = 0;
+  cfg_.ss_number_layers = 3;
+  cfg_.ts_number_layers = 1;
+  cfg_.ts_rate_decimator[0] = 1;
+  cfg_.g_error_resilient = 1;
+  cfg_.g_threads = 1;
+  cfg_.temporal_layering_mode = 0;
+  svc_params_.scaling_factor_num[0] = 72;
+  svc_params_.scaling_factor_den[0] = 288;
+  svc_params_.scaling_factor_num[1] = 144;
+  svc_params_.scaling_factor_den[1] = 288;
+  svc_params_.scaling_factor_num[2] = 288;
+  svc_params_.scaling_factor_den[2] = 288;
+  cfg_.rc_dropframe_thresh = 0;
+  cfg_.kf_max_dist = 9999;
+  number_spatial_layers_ = cfg_.ss_number_layers;
+  number_temporal_layers_ = cfg_.ts_number_layers;
+  ::libvpx_test::I420VideoSource video("niklas_640_480_30.yuv", 640, 480, 30, 1,
+                                       0, 400);
+  cfg_.rc_target_bitrate = 800;
+  ResetModel();
+  dynamic_drop_layer_ = true;
+  assign_layer_bitrates(&cfg_, &svc_params_, cfg_.ss_number_layers,
+                        cfg_.ts_number_layers, cfg_.temporal_layering_mode,
+                        layer_target_avg_bandwidth_, bits_in_buffer_model_);
+  ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
+  // Don't check rate targeting on top spatial layer since it will be skipped
+  // for part of the sequence.
+  CheckLayerRateTargeting(&cfg_, number_spatial_layers_ - 1,
+                          number_temporal_layers_, file_datarate_, 0.78, 1.15);
 }
 
 // Check basic rate targeting for 1 pass CBR SVC: 3 spatial layers and 3
--- a/vp9/encoder/vp9_encoder.c
+++ b/vp9/encoder/vp9_encoder.c
@@ -4360,6 +4360,15 @@
   struct segmentation *const seg = &cm->seg;
   TX_SIZE t;
 
+  // SVC: skip encoding of enhancement layer if the layer target bandwidth = 0.
+  if (cpi->use_svc && cpi->svc.spatial_layer_id > 0 &&
+      !cpi->svc.rc_drop_superframe && cpi->oxcf.target_bandwidth == 0) {
+    cpi->svc.skip_enhancement_layer = 1;
+    vp9_rc_postencode_update_drop_frame(cpi);
+    cpi->ext_refresh_frame_flags_pending = 0;
+    return;
+  }
+
   set_ext_overrides(cpi);
   vpx_clear_system_state();
 
@@ -4451,7 +4460,6 @@
     if (vp9_rc_drop_frame(cpi) ||
         (is_one_pass_cbr_svc(cpi) && cpi->svc.rc_drop_superframe == 1)) {
       vp9_rc_postencode_update_drop_frame(cpi);
-      ++cm->current_video_frame;
       cpi->ext_refresh_frame_flags_pending = 0;
       cpi->svc.rc_drop_superframe = 1;
       cpi->last_frame_dropped = 1;
--- a/vp9/encoder/vp9_ratectrl.c
+++ b/vp9/encoder/vp9_ratectrl.c
@@ -1495,10 +1495,12 @@
 void vp9_rc_postencode_update_drop_frame(VP9_COMP *cpi) {
   // Update buffer level with zero size, update frame counters, and return.
   update_buffer_level(cpi, 0);
+  cpi->common.current_video_frame++;
   cpi->rc.frames_since_key++;
   cpi->rc.frames_to_key--;
   cpi->rc.rc_2_frame = 0;
   cpi->rc.rc_1_frame = 0;
+  cpi->rc.last_avg_frame_bandwidth = cpi->rc.avg_frame_bandwidth;
 }
 
 static int calc_pframe_target_size_one_pass_vbr(const VP9_COMP *const cpi) {
--- a/vp9/encoder/vp9_svc_layercontext.c
+++ b/vp9/encoder/vp9_svc_layercontext.c
@@ -37,6 +37,7 @@
   svc->scaled_one_half = 0;
   svc->current_superframe = 0;
   svc->non_reference_frame = 0;
+  svc->skip_enhancement_layer = 0;
 
   for (i = 0; i < REF_FRAMES; ++i) svc->ref_frame_index[i] = -1;
   for (sl = 0; sl < oxcf->ss_number_layers; ++sl) {
@@ -605,6 +606,7 @@
 int vp9_one_pass_cbr_svc_start_layer(VP9_COMP *const cpi) {
   int width = 0, height = 0;
   LAYER_CONTEXT *lc = NULL;
+  cpi->svc.skip_enhancement_layer = 0;
   if (cpi->svc.number_spatial_layers > 1) cpi->svc.use_base_mv = 1;
   cpi->svc.force_zero_mode_spatial_ref = 1;
   cpi->svc.mi_stride[cpi->svc.spatial_layer_id] = cpi->common.mi_stride;
--- a/vp9/encoder/vp9_svc_layercontext.h
+++ b/vp9/encoder/vp9_svc_layercontext.h
@@ -101,6 +101,8 @@
   int mi_stride[VPX_MAX_LAYERS];
 
   int first_layer_denoise;
+
+  int skip_enhancement_layer;
 } SVC;
 
 struct VP9_COMP;
--- a/vp9/vp9_cx_iface.c
+++ b/vp9/vp9_cx_iface.c
@@ -1213,7 +1213,7 @@
            -1 != vp9_get_compressed_data(cpi, &lib_flags, &size, cx_data,
                                          &dst_time_stamp, &dst_end_time_stamp,
                                          !img)) {
-      if (size) {
+      if (size || (cpi->use_svc && cpi->svc.skip_enhancement_layer)) {
         vpx_codec_cx_pkt_t pkt;
 
 #if CONFIG_SPATIAL_SVC
@@ -1264,7 +1264,7 @@
         pkt.data.frame.height = cpi->common.height;
 
         if (ctx->pending_cx_data) {
-          ctx->pending_frame_sizes[ctx->pending_frame_count++] = size;
+          if (size) ctx->pending_frame_sizes[ctx->pending_frame_count++] = size;
           ctx->pending_frame_magnitude |= size;
           ctx->pending_cx_data_sz += size;
           // write the superframe only for the case when