Home | History | Annotate | Download | only in vp8
      1 // Copyright 2013 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 //
      5 // TODO (pwestin): add a link to the design document describing the generic
      6 // protocol and the VP8 specific details.
      7 #include "media/cast/video_sender/codecs/vp8/vp8_encoder.h"
      8 
      9 #include <vector>
     10 
     11 #include "base/logging.h"
     12 #include "media/base/video_frame.h"
     13 #include "media/cast/cast_defines.h"
     14 #include "third_party/libvpx/source/libvpx/vpx/vp8cx.h"
     15 
     16 namespace media {
     17 namespace cast {
     18 
     19 static const uint32 kMinIntra = 300;
     20 
     21 Vp8Encoder::Vp8Encoder(const VideoSenderConfig& video_config,
     22                        uint8 max_unacked_frames)
     23     : cast_config_(video_config),
     24       use_multiple_video_buffers_(
     25           cast_config_.max_number_of_video_buffers_used ==
     26           kNumberOfVp8VideoBuffers),
     27       max_number_of_repeated_buffers_in_a_row_(
     28           (max_unacked_frames > kNumberOfVp8VideoBuffers) ?
     29           ((max_unacked_frames - 1) / kNumberOfVp8VideoBuffers) : 0),
     30       config_(new vpx_codec_enc_cfg_t()),
     31       encoder_(new vpx_codec_ctx_t()),
     32       // Creating a wrapper to the image - setting image data to NULL. Actual
     33       // pointer will be set during encode. Setting align to 1, as it is
     34       // meaningless (actual memory is not allocated).
     35       raw_image_(vpx_img_wrap(NULL, IMG_FMT_I420, video_config.width,
     36                               video_config.height, 1, NULL)),
     37       key_frame_requested_(true),
     38       timestamp_(0),
     39       last_encoded_frame_id_(kStartFrameId),
     40       number_of_repeated_buffers_(0) {
     41   // TODO(pwestin): we need to figure out how to synchronize the acking with the
     42   // internal state of the encoder, ideally the encoder will tell if we can
     43   // send another frame.
     44   DCHECK(!use_multiple_video_buffers_ ||
     45          max_number_of_repeated_buffers_in_a_row_ == 0) <<  "Invalid config";
     46 
     47   // VP8 have 3 buffers available for prediction, with
     48   // max_number_of_video_buffers_used set to 1 we maximize the coding efficiency
     49   // however in this mode we can not skip frames in the receiver to catch up
     50   // after a temporary network outage; with max_number_of_video_buffers_used
     51   // set to 3 we allow 2 frames to be skipped by the receiver without error
     52   // propagation.
     53   DCHECK(cast_config_.max_number_of_video_buffers_used == 1 ||
     54          cast_config_.max_number_of_video_buffers_used ==
     55              kNumberOfVp8VideoBuffers) << "Invalid argument";
     56 
     57   for (int i = 0; i < kNumberOfVp8VideoBuffers; ++i) {
     58     acked_frame_buffers_[i] = true;
     59     used_buffers_frame_id_[i] = kStartFrameId;
     60   }
     61   InitEncode(video_config.number_of_cores);
     62 }
     63 
     64 Vp8Encoder::~Vp8Encoder() {
     65   vpx_codec_destroy(encoder_);
     66   vpx_img_free(raw_image_);
     67 }
     68 
     69 void Vp8Encoder::InitEncode(int number_of_cores) {
     70   // Populate encoder configuration with default values.
     71   if (vpx_codec_enc_config_default(vpx_codec_vp8_cx(), config_.get(), 0)) {
     72     DCHECK(false) << "Invalid return value";
     73   }
     74   config_->g_w = cast_config_.width;
     75   config_->g_h = cast_config_.height;
     76   config_->rc_target_bitrate = cast_config_.start_bitrate / 1000;  // In kbit/s.
     77 
     78   // Setting the codec time base.
     79   config_->g_timebase.num = 1;
     80   config_->g_timebase.den = kVideoFrequency;
     81   config_->g_lag_in_frames = 0;
     82   config_->kf_mode = VPX_KF_DISABLED;
     83   if (use_multiple_video_buffers_) {
     84     // We must enable error resilience when we use multiple buffers, due to
     85     // codec requirements.
     86     config_->g_error_resilient = 1;
     87   }
     88 
     89   if (cast_config_.width * cast_config_.height > 640 * 480
     90       && number_of_cores >= 2) {
     91     config_->g_threads = 2;  // 2 threads for qHD/HD.
     92   } else {
     93     config_->g_threads = 1;  // 1 thread for VGA or less.
     94   }
     95 
     96   // Rate control settings.
     97   // TODO(pwestin): revisit these constants. Currently identical to webrtc.
     98   config_->rc_dropframe_thresh = 30;
     99   config_->rc_end_usage = VPX_CBR;
    100   config_->g_pass = VPX_RC_ONE_PASS;
    101   config_->rc_resize_allowed = 0;
    102   config_->rc_min_quantizer = cast_config_.min_qp;
    103   config_->rc_max_quantizer = cast_config_.max_qp;
    104   config_->rc_undershoot_pct = 100;
    105   config_->rc_overshoot_pct = 15;
    106   config_->rc_buf_initial_sz = 500;
    107   config_->rc_buf_optimal_sz = 600;
    108   config_->rc_buf_sz = 1000;
    109 
    110   // set the maximum target size of any key-frame.
    111   uint32 rc_max_intra_target = MaxIntraTarget(config_->rc_buf_optimal_sz);
    112   vpx_codec_flags_t flags = 0;
    113   // TODO(mikhal): Tune settings.
    114   if (vpx_codec_enc_init(encoder_, vpx_codec_vp8_cx(), config_.get(), flags)) {
    115     DCHECK(false) << "Invalid return value";
    116   }
    117   vpx_codec_control(encoder_, VP8E_SET_STATIC_THRESHOLD, 1);
    118   vpx_codec_control(encoder_, VP8E_SET_NOISE_SENSITIVITY, 0);
    119   vpx_codec_control(encoder_, VP8E_SET_CPUUSED, -6);
    120   vpx_codec_control(encoder_, VP8E_SET_MAX_INTRA_BITRATE_PCT,
    121                     rc_max_intra_target);
    122 }
    123 
    124 bool Vp8Encoder::Encode(const scoped_refptr<media::VideoFrame>& video_frame,
    125                         EncodedVideoFrame* encoded_image) {
    126   // Image in vpx_image_t format.
    127   // Input image is const. VP8's raw image is not defined as const.
    128   raw_image_->planes[PLANE_Y] =
    129       const_cast<uint8*>(video_frame->data(VideoFrame::kYPlane));
    130   raw_image_->planes[PLANE_U] =
    131       const_cast<uint8*>(video_frame->data(VideoFrame::kUPlane));
    132   raw_image_->planes[PLANE_V] =
    133       const_cast<uint8*>(video_frame->data(VideoFrame::kVPlane));
    134 
    135   raw_image_->stride[VPX_PLANE_Y] = video_frame->stride(VideoFrame::kYPlane);
    136   raw_image_->stride[VPX_PLANE_U] = video_frame->stride(VideoFrame::kUPlane);
    137   raw_image_->stride[VPX_PLANE_V] = video_frame->stride(VideoFrame::kVPlane);
    138 
    139   uint8 latest_frame_id_to_reference;
    140   Vp8Buffers buffer_to_update;
    141   vpx_codec_flags_t flags = 0;
    142   if (key_frame_requested_) {
    143     flags = VPX_EFLAG_FORCE_KF;
    144     // Self reference.
    145     latest_frame_id_to_reference =
    146         static_cast<uint8>(last_encoded_frame_id_ + 1);
    147     // We can pick any buffer as buffer_to_update since we update
    148     // them all.
    149     buffer_to_update = kLastBuffer;
    150   } else {
    151     // Reference all acked frames (buffers).
    152     latest_frame_id_to_reference = GetLatestFrameIdToReference();
    153     GetCodecReferenceFlags(&flags);
    154     buffer_to_update = GetNextBufferToUpdate();
    155     GetCodecUpdateFlags(buffer_to_update, &flags);
    156   }
    157 
    158   // Note: The duration does not reflect the real time between frames. This is
    159   // done to keep the encoder happy.
    160   uint32 duration = kVideoFrequency / cast_config_.max_frame_rate;
    161   if (vpx_codec_encode(encoder_, raw_image_, timestamp_, duration, flags,
    162                        VPX_DL_REALTIME)) {
    163     return false;
    164   }
    165   timestamp_ += duration;
    166 
    167   // Get encoded frame.
    168   const vpx_codec_cx_pkt_t *pkt = NULL;
    169   vpx_codec_iter_t iter = NULL;
    170   size_t total_size = 0;
    171   while ((pkt = vpx_codec_get_cx_data(encoder_, &iter)) != NULL) {
    172     if (pkt->kind == VPX_CODEC_CX_FRAME_PKT) {
    173       total_size += pkt->data.frame.sz;
    174       encoded_image->data.reserve(total_size);
    175       encoded_image->data.insert(
    176           encoded_image->data.end(),
    177           static_cast<const uint8*>(pkt->data.frame.buf),
    178           static_cast<const uint8*>(pkt->data.frame.buf) +
    179               pkt->data.frame.sz);
    180       if (pkt->data.frame.flags & VPX_FRAME_IS_KEY) {
    181         encoded_image->key_frame = true;
    182       } else {
    183         encoded_image->key_frame = false;
    184       }
    185     }
    186   }
    187   // Don't update frame_id for zero size frames.
    188   if (total_size == 0) return true;
    189 
    190   // Populate the encoded frame.
    191   encoded_image->codec = kVp8;
    192   encoded_image->last_referenced_frame_id = latest_frame_id_to_reference;
    193   encoded_image->frame_id = ++last_encoded_frame_id_;
    194 
    195   VLOG(1) << "VP8 encoded frame:" << static_cast<int>(encoded_image->frame_id)
    196           << " sized:" << total_size;
    197 
    198   if (encoded_image->key_frame) {
    199     key_frame_requested_ = false;
    200 
    201     for (int i = 0; i < kNumberOfVp8VideoBuffers; ++i) {
    202       used_buffers_frame_id_[i] = encoded_image->frame_id;
    203     }
    204     // We can pick any buffer as last_used_vp8_buffer_ since we update
    205     // them all.
    206     last_used_vp8_buffer_ = buffer_to_update;
    207   } else {
    208     if (buffer_to_update != kNoBuffer) {
    209       acked_frame_buffers_[buffer_to_update] = false;
    210       used_buffers_frame_id_[buffer_to_update] = encoded_image->frame_id;
    211       last_used_vp8_buffer_ = buffer_to_update;
    212     }
    213   }
    214   return true;
    215 }
    216 
    217 void Vp8Encoder::GetCodecReferenceFlags(vpx_codec_flags_t* flags) {
    218   if (!use_multiple_video_buffers_) return;
    219 
    220   // We need to reference something.
    221   DCHECK(acked_frame_buffers_[kAltRefBuffer] ||
    222          acked_frame_buffers_[kGoldenBuffer] ||
    223          acked_frame_buffers_[kLastBuffer]) << "Invalid state";
    224 
    225   if (!acked_frame_buffers_[kAltRefBuffer]) {
    226     *flags |= VP8_EFLAG_NO_REF_ARF;
    227   }
    228   if (!acked_frame_buffers_[kGoldenBuffer]) {
    229     *flags |= VP8_EFLAG_NO_REF_GF;
    230   }
    231   if (!acked_frame_buffers_[kLastBuffer]) {
    232     *flags |= VP8_EFLAG_NO_REF_LAST;
    233   }
    234 }
    235 
    236 uint32 Vp8Encoder::GetLatestFrameIdToReference() {
    237   if (!use_multiple_video_buffers_) return last_encoded_frame_id_;
    238 
    239   int latest_frame_id_to_reference = -1;
    240   if (acked_frame_buffers_[kAltRefBuffer]) {
    241     latest_frame_id_to_reference = used_buffers_frame_id_[kAltRefBuffer];
    242   }
    243   if (acked_frame_buffers_[kGoldenBuffer]) {
    244     if (latest_frame_id_to_reference == -1) {
    245       latest_frame_id_to_reference = used_buffers_frame_id_[kGoldenBuffer];
    246     } else {
    247       if (IsNewerFrameId(used_buffers_frame_id_[kGoldenBuffer],
    248                          latest_frame_id_to_reference)) {
    249         latest_frame_id_to_reference = used_buffers_frame_id_[kGoldenBuffer];
    250       }
    251     }
    252   }
    253   if (acked_frame_buffers_[kLastBuffer]) {
    254     if (latest_frame_id_to_reference == -1) {
    255       latest_frame_id_to_reference = used_buffers_frame_id_[kLastBuffer];
    256     } else {
    257       if (IsNewerFrameId(used_buffers_frame_id_[kLastBuffer],
    258                          latest_frame_id_to_reference)) {
    259         latest_frame_id_to_reference = used_buffers_frame_id_[kLastBuffer];
    260       }
    261     }
    262   }
    263   DCHECK(latest_frame_id_to_reference != -1) << "Invalid state";
    264   return static_cast<uint32>(latest_frame_id_to_reference);
    265 }
    266 
    267 Vp8Encoder::Vp8Buffers Vp8Encoder::GetNextBufferToUpdate() {
    268   // Update at most one buffer, except for key-frames.
    269 
    270   Vp8Buffers buffer_to_update;
    271   if (number_of_repeated_buffers_ < max_number_of_repeated_buffers_in_a_row_) {
    272     // TODO(pwestin): experiment with this. The issue with only this change is
    273     // that we can end up with only 4 frames in flight when we expect 6.
    274     // buffer_to_update = last_used_vp8_buffer_;
    275     buffer_to_update = kNoBuffer;
    276     ++number_of_repeated_buffers_;
    277   } else {
    278     number_of_repeated_buffers_ = 0;
    279     switch (last_used_vp8_buffer_) {
    280       case kAltRefBuffer:
    281         buffer_to_update = kLastBuffer;
    282         VLOG(1) << "VP8 update last buffer";
    283         break;
    284       case kLastBuffer:
    285         buffer_to_update = kGoldenBuffer;
    286         VLOG(1) << "VP8 update golden buffer";
    287         break;
    288       case kGoldenBuffer:
    289         buffer_to_update = kAltRefBuffer;
    290         VLOG(1) << "VP8 update alt-ref buffer";
    291         break;
    292       case kNoBuffer:
    293         DCHECK(false) << "Invalid state";
    294         break;
    295     }
    296   }
    297   return buffer_to_update;
    298 }
    299 
    300 void Vp8Encoder::GetCodecUpdateFlags(Vp8Buffers buffer_to_update,
    301                                      vpx_codec_flags_t* flags) {
    302   if (!use_multiple_video_buffers_) return;
    303 
    304   // Update at most one buffer, except for key-frames.
    305   switch (buffer_to_update) {
    306     case kAltRefBuffer:
    307       *flags |= VP8_EFLAG_NO_UPD_GF;
    308       *flags |= VP8_EFLAG_NO_UPD_LAST;
    309       break;
    310     case kLastBuffer:
    311       *flags |= VP8_EFLAG_NO_UPD_GF;
    312       *flags |= VP8_EFLAG_NO_UPD_ARF;
    313       break;
    314     case kGoldenBuffer:
    315       *flags |= VP8_EFLAG_NO_UPD_ARF;
    316       *flags |= VP8_EFLAG_NO_UPD_LAST;
    317       break;
    318     case kNoBuffer:
    319       *flags |= VP8_EFLAG_NO_UPD_ARF;
    320       *flags |= VP8_EFLAG_NO_UPD_GF;
    321       *flags |= VP8_EFLAG_NO_UPD_LAST;
    322       *flags |= VP8_EFLAG_NO_UPD_ENTROPY;
    323       break;
    324   }
    325 }
    326 
    327 void Vp8Encoder::UpdateRates(uint32 new_bitrate) {
    328   uint32 new_bitrate_kbit = new_bitrate / 1000;
    329   if (config_->rc_target_bitrate == new_bitrate_kbit) return;
    330 
    331   config_->rc_target_bitrate = new_bitrate_kbit;
    332 
    333   // Update encoder context.
    334   if (vpx_codec_enc_config_set(encoder_, config_.get())) {
    335     DCHECK(false) << "Invalid return value";
    336   }
    337 }
    338 
    339 void Vp8Encoder::LatestFrameIdToReference(uint32 frame_id) {
    340   if (!use_multiple_video_buffers_) return;
    341 
    342   VLOG(1) << "VP8 ok to reference frame:" << static_cast<int>(frame_id);
    343   for (int i = 0; i < kNumberOfVp8VideoBuffers; ++i) {
    344     if (frame_id == used_buffers_frame_id_[i]) {
    345       acked_frame_buffers_[i] = true;
    346     }
    347   }
    348 }
    349 
    350 void Vp8Encoder::GenerateKeyFrame() {
    351   key_frame_requested_ = true;
    352 }
    353 
    354 // Calculate the max size of the key frame relative to a normal delta frame.
    355 uint32 Vp8Encoder::MaxIntraTarget(uint32 optimal_buffer_size_ms) const {
    356   // Set max to the optimal buffer level (normalized by target BR),
    357   // and scaled by a scale_parameter.
    358   // Max target size = scalePar * optimalBufferSize * targetBR[Kbps].
    359   // This values is presented in percentage of perFrameBw:
    360   // perFrameBw = targetBR[Kbps] * 1000 / frameRate.
    361   // The target in % is as follows:
    362 
    363   float scale_parameter = 0.5;
    364   uint32 target_pct = optimal_buffer_size_ms * scale_parameter *
    365       cast_config_.max_frame_rate / 10;
    366 
    367   // Don't go below 3 times the per frame bandwidth.
    368   return std::max(target_pct, kMinIntra);
    369 }
    370 
    371 }  // namespace cast
    372 }  // namespace media
    373