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.
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"
11 #include "base/logging.h"
12 #include "media/cast/cast_defines.h"
13 #include "media/cast/rtp_common/rtp_defines.h"
14 #include "third_party/libvpx/source/libvpx/vpx/vp8cx.h"
19 static const uint32 kMinIntra = 300;
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),
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";
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
53 DCHECK(cast_config_.max_number_of_video_buffers_used == 1 ||
54 cast_config_.max_number_of_video_buffers_used ==
55 kNumberOfVp8VideoBuffers) << "Invalid argument";
57 for (int i = 0; i < kNumberOfVp8VideoBuffers; ++i) {
58 acked_frame_buffers_[i] = true;
59 used_buffers_frame_id_[i] = kStartFrameId;
61 InitEncode(video_config.number_of_cores);
64 Vp8Encoder::~Vp8Encoder() {
65 vpx_codec_destroy(encoder_);
66 vpx_img_free(raw_image_);
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";
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.
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;
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.
93 config_->g_threads = 1; // 1 thread for VGA or less.
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;
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";
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);
124 bool Vp8Encoder::Encode(const I420VideoFrame& input_image,
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] = const_cast<uint8*>(input_image.y_plane.data);
129 raw_image_->planes[PLANE_U] = const_cast<uint8*>(input_image.u_plane.data);
130 raw_image_->planes[PLANE_V] = const_cast<uint8*>(input_image.v_plane.data);
132 raw_image_->stride[VPX_PLANE_Y] = input_image.y_plane.stride;
133 raw_image_->stride[VPX_PLANE_U] = input_image.u_plane.stride;
134 raw_image_->stride[VPX_PLANE_V] = input_image.v_plane.stride;
136 uint8 latest_frame_id_to_reference;
137 Vp8Buffers buffer_to_update;
138 vpx_codec_flags_t flags = 0;
139 if (key_frame_requested_) {
140 flags = VPX_EFLAG_FORCE_KF;
142 latest_frame_id_to_reference =
143 static_cast<uint8>(last_encoded_frame_id_ + 1);
144 // We can pick any buffer as buffer_to_update since we update
146 buffer_to_update = kLastBuffer;
148 // Reference all acked frames (buffers).
149 latest_frame_id_to_reference = GetLatestFrameIdToReference();
150 GetCodecReferenceFlags(&flags);
151 buffer_to_update = GetNextBufferToUpdate();
152 GetCodecUpdateFlags(buffer_to_update, &flags);
155 // Note: The duration does not reflect the real time between frames. This is
156 // done to keep the encoder happy.
157 uint32 duration = kVideoFrequency / cast_config_.max_frame_rate;
158 if (vpx_codec_encode(encoder_, raw_image_, timestamp_, duration, flags,
162 timestamp_ += duration;
164 // Get encoded frame.
165 const vpx_codec_cx_pkt_t *pkt = NULL;
166 vpx_codec_iter_t iter = NULL;
167 size_t total_size = 0;
168 while ((pkt = vpx_codec_get_cx_data(encoder_, &iter)) != NULL) {
169 if (pkt->kind == VPX_CODEC_CX_FRAME_PKT) {
170 total_size += pkt->data.frame.sz;
171 encoded_image->data.reserve(total_size);
172 encoded_image->data.insert(
173 encoded_image->data.end(),
174 static_cast<const uint8*>(pkt->data.frame.buf),
175 static_cast<const uint8*>(pkt->data.frame.buf) +
177 if (pkt->data.frame.flags & VPX_FRAME_IS_KEY) {
178 encoded_image->key_frame = true;
180 encoded_image->key_frame = false;
184 // Don't update frame_id for zero size frames.
185 if (total_size == 0) return true;
187 // Populate the encoded frame.
188 encoded_image->codec = kVp8;
189 encoded_image->last_referenced_frame_id = latest_frame_id_to_reference;
190 encoded_image->frame_id = ++last_encoded_frame_id_;
192 VLOG(1) << "VP8 encoded frame:" << static_cast<int>(encoded_image->frame_id)
193 << " sized:" << total_size;
195 if (encoded_image->key_frame) {
196 key_frame_requested_ = false;
198 for (int i = 0; i < kNumberOfVp8VideoBuffers; ++i) {
199 used_buffers_frame_id_[i] = encoded_image->frame_id;
201 // We can pick any buffer as last_used_vp8_buffer_ since we update
203 last_used_vp8_buffer_ = buffer_to_update;
205 if (buffer_to_update != kNoBuffer) {
206 acked_frame_buffers_[buffer_to_update] = false;
207 used_buffers_frame_id_[buffer_to_update] = encoded_image->frame_id;
208 last_used_vp8_buffer_ = buffer_to_update;
214 void Vp8Encoder::GetCodecReferenceFlags(vpx_codec_flags_t* flags) {
215 if (!use_multiple_video_buffers_) return;
217 // We need to reference something.
218 DCHECK(acked_frame_buffers_[kAltRefBuffer] ||
219 acked_frame_buffers_[kGoldenBuffer] ||
220 acked_frame_buffers_[kLastBuffer]) << "Invalid state";
222 if (!acked_frame_buffers_[kAltRefBuffer]) {
223 *flags |= VP8_EFLAG_NO_REF_ARF;
225 if (!acked_frame_buffers_[kGoldenBuffer]) {
226 *flags |= VP8_EFLAG_NO_REF_GF;
228 if (!acked_frame_buffers_[kLastBuffer]) {
229 *flags |= VP8_EFLAG_NO_REF_LAST;
233 uint8 Vp8Encoder::GetLatestFrameIdToReference() {
234 if (!use_multiple_video_buffers_) return last_encoded_frame_id_;
236 int latest_frame_id_to_reference = -1;
237 if (acked_frame_buffers_[kAltRefBuffer]) {
238 latest_frame_id_to_reference = used_buffers_frame_id_[kAltRefBuffer];
240 if (acked_frame_buffers_[kGoldenBuffer]) {
241 if (latest_frame_id_to_reference == -1) {
242 latest_frame_id_to_reference = used_buffers_frame_id_[kGoldenBuffer];
244 if (IsNewerFrameId(used_buffers_frame_id_[kGoldenBuffer],
245 latest_frame_id_to_reference)) {
246 latest_frame_id_to_reference = used_buffers_frame_id_[kGoldenBuffer];
250 if (acked_frame_buffers_[kLastBuffer]) {
251 if (latest_frame_id_to_reference == -1) {
252 latest_frame_id_to_reference = used_buffers_frame_id_[kLastBuffer];
254 if (IsNewerFrameId(used_buffers_frame_id_[kLastBuffer],
255 latest_frame_id_to_reference)) {
256 latest_frame_id_to_reference = used_buffers_frame_id_[kLastBuffer];
260 DCHECK(latest_frame_id_to_reference != -1) << "Invalid state";
261 return static_cast<uint8>(latest_frame_id_to_reference);
264 Vp8Encoder::Vp8Buffers Vp8Encoder::GetNextBufferToUpdate() {
265 // Update at most one buffer, except for key-frames.
267 Vp8Buffers buffer_to_update;
268 if (number_of_repeated_buffers_ < max_number_of_repeated_buffers_in_a_row_) {
269 // TODO(pwestin): experiment with this. The issue with only this change is
270 // that we can end up with only 4 frames in flight when we expect 6.
271 // buffer_to_update = last_used_vp8_buffer_;
272 buffer_to_update = kNoBuffer;
273 ++number_of_repeated_buffers_;
275 number_of_repeated_buffers_ = 0;
276 switch (last_used_vp8_buffer_) {
278 buffer_to_update = kLastBuffer;
279 VLOG(1) << "VP8 update last buffer";
282 buffer_to_update = kGoldenBuffer;
283 VLOG(1) << "VP8 update golden buffer";
286 buffer_to_update = kAltRefBuffer;
287 VLOG(1) << "VP8 update alt-ref buffer";
290 DCHECK(false) << "Invalid state";
294 return buffer_to_update;
297 void Vp8Encoder::GetCodecUpdateFlags(Vp8Buffers buffer_to_update,
298 vpx_codec_flags_t* flags) {
299 if (!use_multiple_video_buffers_) return;
301 // Update at most one buffer, except for key-frames.
302 switch (buffer_to_update) {
304 *flags |= VP8_EFLAG_NO_UPD_GF;
305 *flags |= VP8_EFLAG_NO_UPD_LAST;
308 *flags |= VP8_EFLAG_NO_UPD_GF;
309 *flags |= VP8_EFLAG_NO_UPD_ARF;
312 *flags |= VP8_EFLAG_NO_UPD_ARF;
313 *flags |= VP8_EFLAG_NO_UPD_LAST;
316 *flags |= VP8_EFLAG_NO_UPD_ARF;
317 *flags |= VP8_EFLAG_NO_UPD_GF;
318 *flags |= VP8_EFLAG_NO_UPD_LAST;
319 *flags |= VP8_EFLAG_NO_UPD_ENTROPY;
324 void Vp8Encoder::UpdateRates(uint32 new_bitrate) {
325 uint32 new_bitrate_kbit = new_bitrate / 1000;
326 if (config_->rc_target_bitrate == new_bitrate_kbit) return;
328 config_->rc_target_bitrate = new_bitrate_kbit;
330 // Update encoder context.
331 if (vpx_codec_enc_config_set(encoder_, config_.get())) {
332 DCHECK(false) << "Invalid return value";
336 void Vp8Encoder::LatestFrameIdToReference(uint8 frame_id) {
337 if (!use_multiple_video_buffers_) return;
339 VLOG(1) << "VP8 ok to reference frame:" << static_cast<int>(frame_id);
340 for (int i = 0; i < kNumberOfVp8VideoBuffers; ++i) {
341 if (frame_id == used_buffers_frame_id_[i]) {
342 acked_frame_buffers_[i] = true;
347 void Vp8Encoder::GenerateKeyFrame() {
348 key_frame_requested_ = true;
351 // Calculate the max size of the key frame relative to a normal delta frame.
352 uint32 Vp8Encoder::MaxIntraTarget(uint32 optimal_buffer_size_ms) const {
353 // Set max to the optimal buffer level (normalized by target BR),
354 // and scaled by a scale_parameter.
355 // Max target size = scalePar * optimalBufferSize * targetBR[Kbps].
356 // This values is presented in percentage of perFrameBw:
357 // perFrameBw = targetBR[Kbps] * 1000 / frameRate.
358 // The target in % is as follows:
360 float scale_parameter = 0.5;
361 uint32 target_pct = optimal_buffer_size_ms * scale_parameter *
362 cast_config_.max_frame_rate / 10;
364 // Don't go below 3 times the per frame bandwidth.
365 return std::max(target_pct, kMinIntra);