Bug 1315283: allow VP9 encoder in webrtc to reconfigure if the input resolution changes r=TD-Linux
authorRandell Jesup <rjesup@jesup.org>
Sat, 12 Nov 2016 02:57:17 -0500
changeset 352374 8b0d24bbb59d8cddbf8f3f74ffd84695a056214b
parent 352373 17ce9b584392fdfeee015743c1ccba2d546bacc1
child 352375 ddec56f2c469512ba99e424540f6f77a447fdea0
push id6795
push userjlund@mozilla.com
push dateMon, 23 Jan 2017 14:19:46 +0000
treeherdermozilla-esr52@76101b503191 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersTD-Linux
bugs1315283
milestone52.0a1
Bug 1315283: allow VP9 encoder in webrtc to reconfigure if the input resolution changes r=TD-Linux
media/webrtc/trunk/webrtc/modules/video_coding/codecs/vp9/vp9_impl.cc
media/webrtc/trunk/webrtc/modules/video_coding/codecs/vp9/vp9_impl.h
--- a/media/webrtc/trunk/webrtc/modules/video_coding/codecs/vp9/vp9_impl.cc
+++ b/media/webrtc/trunk/webrtc/modules/video_coding/codecs/vp9/vp9_impl.cc
@@ -75,16 +75,17 @@ VP9EncoderImpl::VP9EncoderImpl()
       encoder_(NULL),
       config_(NULL),
       raw_(NULL),
       input_image_(NULL),
       tl0_pic_idx_(0),
       frames_since_kf_(0),
       num_temporal_layers_(0),
       num_spatial_layers_(0),
+      num_cores_(0),
       frames_encoded_(0),
       // Use two spatial when screensharing with flexible mode.
       spatial_layer_(new ScreenshareLayersVP9(2)) {
   memset(&codec_, 0, sizeof(codec_));
   uint32_t seed = static_cast<uint32_t>(TickTime::MillisecondTimestamp());
   srand(seed);
 }
 
@@ -247,17 +248,17 @@ int VP9EncoderImpl::InitEncode(const Vid
   }
   // Allow zero to represent an unspecified maxBitRate
   if (inst->maxBitrate > 0 && inst->startBitrate > inst->maxBitrate) {
     return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
   }
   if (inst->width < 1 || inst->height < 1) {
     return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
   }
-  if (number_of_cores < 1) {
+  if (number_of_cores < 1 || number_of_cores > UINT8_MAX) {
     return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
   }
   if (inst->codecSpecific.VP9.numberOfTemporalLayers > 3) {
     return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
   }
   // libvpx currently supports only one or two spatial layers.
   if (inst->codecSpecific.VP9.numberOfSpatialLayers > 2) {
     return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
@@ -273,16 +274,17 @@ int VP9EncoderImpl::InitEncode(const Vid
   if (config_ == NULL) {
     config_ = new vpx_codec_enc_cfg_t;
   }
   timestamp_ = 0;
   if (&codec_ != inst) {
     codec_ = *inst;
   }
 
+  num_cores_ = number_of_cores;
   num_spatial_layers_ = inst->codecSpecific.VP9.numberOfSpatialLayers;
   num_temporal_layers_ = inst->codecSpecific.VP9.numberOfTemporalLayers;
   if (num_temporal_layers_ == 0)
     num_temporal_layers_ = 1;
 
   // Random start 16 bits is enough.
   picture_id_ = static_cast<uint16_t>(rand()) & 0x7FFF;
   // Allocate memory for encoded image
@@ -330,17 +332,17 @@ int VP9EncoderImpl::InitEncode(const Vid
   } else {
     config_->kf_mode = VPX_KF_DISABLED;
   }
   config_->rc_resize_allowed = inst->codecSpecific.VP9.automaticResizeOn ?
       1 : 0;
   // Determine number of threads based on the image size and #cores.
   config_->g_threads = NumberOfThreads(config_->g_w,
                                        config_->g_h,
-                                       number_of_cores);
+                                       num_cores_);
 
   cpu_speed_ = GetCpuSpeed(config_->g_w, config_->g_h);
 
   // TODO(asapersson): Check configuration of temporal switch up and increase
   // pattern length.
   is_flexible_mode_ = inst->codecSpecific.VP9.flexibleMode;
   if (is_flexible_mode_) {
     config_->temporal_layering_mode = VP9E_TEMPORAL_LAYERING_MODE_BYPASS;
@@ -501,16 +503,23 @@ int VP9EncoderImpl::Encode(const I420Vid
   if (encoded_complete_callback_ == NULL) {
     return WEBRTC_VIDEO_CODEC_UNINITIALIZED;
   }
   VideoFrameType frame_type = kDeltaFrame;
   // We only support one stream at the moment.
   if (frame_types && frame_types->size() > 0) {
     frame_type = (*frame_types)[0];
   }
+  if (input_image.width() != codec_.width ||
+      input_image.height() != codec_.height) {
+    int ret = UpdateCodecFrameSize(input_image);
+    if (ret < 0) {
+      return ret;
+    }
+  }
   DCHECK_EQ(input_image.width(), static_cast<int>(raw_->d_w));
   DCHECK_EQ(input_image.height(), static_cast<int>(raw_->d_h));
 
   // Set input image for use in the callback.
   // This was necessary since you need some information from input_image.
   // You can save only the necessary information (such as timestamp) instead of
   // doing this.
   input_image_ = &input_image;
@@ -561,16 +570,54 @@ int VP9EncoderImpl::Encode(const I420Vid
                        VPX_DL_REALTIME)) {
     return WEBRTC_VIDEO_CODEC_ERROR;
   }
   timestamp_ += duration;
 
   return WEBRTC_VIDEO_CODEC_OK;
 }
 
+int VP9EncoderImpl::UpdateCodecFrameSize(
+    const I420VideoFrame& input_image) {
+  fprintf(stderr, "Reconfiging VP( from %dx%d to %dx%d\n",
+          codec_.width, codec_.height, input_image.width(), input_image.height());
+  // Preserve latest bitrate/framerate setting
+  uint32_t old_bitrate_kbit = config_->rc_target_bitrate;
+  uint32_t old_framerate = codec_.maxFramerate;
+
+  codec_.width = input_image.width();
+  codec_.height = input_image.height();
+
+  vpx_img_free(raw_);
+  raw_ = vpx_img_wrap(NULL, VPX_IMG_FMT_I420, codec_.width, codec_.height,
+                      1, NULL);
+  // Update encoder context for new frame size.
+  config_->g_w = codec_.width;
+  config_->g_h = codec_.height;
+
+  // Determine number of threads based on the image size and #cores.
+  config_->g_threads = NumberOfThreads(codec_.width, codec_.height,
+                                       num_cores_);
+  // Update the cpu_speed setting for resolution change.
+  cpu_speed_ = GetCpuSpeed(codec_.width, codec_.height);
+
+  // NOTE: We would like to do this the same way vp8 does it
+  // (with vpx_codec_enc_config_set()), but that causes asserts
+  // in AQ 3 (cyclic); and in AQ 0 it works, but on a resize to smaller
+  // than 1/2 x 1/2 original it asserts in convolve().  Given these
+  // bugs in trying to do it the "right" way, we basically re-do
+  // the initialization.
+  vpx_codec_destroy(encoder_); // clean up old state
+  int result = InitAndSetControlSettings(&codec_);
+  if (result == WEBRTC_VIDEO_CODEC_OK) {
+    return SetRates(old_bitrate_kbit, old_framerate);
+  }
+  return result;
+}
+
 void VP9EncoderImpl::PopulateCodecSpecific(CodecSpecificInfo* codec_specific,
                                        const vpx_codec_cx_pkt& pkt,
                                        uint32_t timestamp) {
   assert(codec_specific != NULL);
   codec_specific->codecType = kVideoCodecVP9;
   CodecSpecificInfoVP9 *vp9_info = &(codec_specific->codecSpecific.VP9);
   // TODO(asapersson): Set correct values.
   vp9_info->inter_pic_predicted =
--- a/media/webrtc/trunk/webrtc/modules/video_coding/codecs/vp9/vp9_impl.h
+++ b/media/webrtc/trunk/webrtc/modules/video_coding/codecs/vp9/vp9_impl.h
@@ -63,16 +63,19 @@ class VP9EncoderImpl : public VP9Encoder
 
  private:
   // Determine number of encoder threads to use.
   int NumberOfThreads(int width, int height, int number_of_cores);
 
   // Call encoder initialize function and set control settings.
   int InitAndSetControlSettings(const VideoCodec* inst);
 
+  // Update frame size for codec.
+  int UpdateCodecFrameSize(const I420VideoFrame& input_image);
+
   void PopulateCodecSpecific(CodecSpecificInfo* codec_specific,
                              const vpx_codec_cx_pkt& pkt,
                              uint32_t timestamp);
 
   bool ExplicitlyConfiguredSpatialLayers() const;
   bool SetSvcRates();
 
 #ifdef LIBVPX_SVC
@@ -116,16 +119,17 @@ class VP9EncoderImpl : public VP9Encoder
 #endif
   const I420VideoFrame* input_image_;
   GofInfoVP9 gof_;       // Contains each frame's temporal information for
                          // non-flexible mode.
   uint8_t tl0_pic_idx_;  // Only used in non-flexible mode.
   size_t frames_since_kf_;
   uint8_t num_temporal_layers_;
   uint8_t num_spatial_layers_;
+  uint8_t num_cores_;
 
   // Used for flexible mode.
   bool is_flexible_mode_;
   int64_t buffer_updated_at_frame_[kNumVp9Buffers];
   int64_t frames_encoded_;
   uint8_t num_ref_pics_[kMaxVp9NumberOfSpatialLayers];
   uint8_t p_diff_[kMaxVp9NumberOfSpatialLayers][kMaxVp9RefPics];
   rtc::scoped_ptr<ScreenshareLayersVP9> spatial_layer_;