| OLD | NEW |
| (Empty) |
| 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 "media/cast/transport/cast_transport_config.h" | |
| 15 #include "third_party/libvpx/source/libvpx/vpx/vp8cx.h" | |
| 16 | |
| 17 namespace media { | |
| 18 namespace cast { | |
| 19 | |
| 20 static const uint32 kMinIntra = 300; | |
| 21 | |
| 22 static int ComputeMaxNumOfRepeatedBuffes(int max_unacked_frames) { | |
| 23 if (max_unacked_frames > kNumberOfVp8VideoBuffers) | |
| 24 return (max_unacked_frames - 1) / kNumberOfVp8VideoBuffers; | |
| 25 | |
| 26 return 0; | |
| 27 } | |
| 28 | |
| 29 Vp8Encoder::Vp8Encoder(const VideoSenderConfig& video_config, | |
| 30 int max_unacked_frames) | |
| 31 : cast_config_(video_config), | |
| 32 use_multiple_video_buffers_( | |
| 33 cast_config_.max_number_of_video_buffers_used == | |
| 34 kNumberOfVp8VideoBuffers), | |
| 35 max_number_of_repeated_buffers_in_a_row_( | |
| 36 ComputeMaxNumOfRepeatedBuffes(max_unacked_frames)), | |
| 37 key_frame_requested_(true), | |
| 38 first_frame_received_(false), | |
| 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) | |
| 46 << "Invalid config"; | |
| 47 | |
| 48 // VP8 have 3 buffers available for prediction, with | |
| 49 // max_number_of_video_buffers_used set to 1 we maximize the coding efficiency | |
| 50 // however in this mode we can not skip frames in the receiver to catch up | |
| 51 // after a temporary network outage; with max_number_of_video_buffers_used | |
| 52 // set to 3 we allow 2 frames to be skipped by the receiver without error | |
| 53 // propagation. | |
| 54 DCHECK(cast_config_.max_number_of_video_buffers_used == 1 || | |
| 55 cast_config_.max_number_of_video_buffers_used == | |
| 56 kNumberOfVp8VideoBuffers) | |
| 57 << "Invalid argument"; | |
| 58 | |
| 59 thread_checker_.DetachFromThread(); | |
| 60 } | |
| 61 | |
| 62 Vp8Encoder::~Vp8Encoder() { | |
| 63 vpx_codec_destroy(encoder_.get()); | |
| 64 vpx_img_free(raw_image_); | |
| 65 } | |
| 66 | |
| 67 void Vp8Encoder::Initialize() { | |
| 68 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 69 config_.reset(new vpx_codec_enc_cfg_t()); | |
| 70 encoder_.reset(new vpx_codec_ctx_t()); | |
| 71 | |
| 72 // Creating a wrapper to the image - setting image data to NULL. Actual | |
| 73 // pointer will be set during encode. Setting align to 1, as it is | |
| 74 // meaningless (actual memory is not allocated). | |
| 75 raw_image_ = vpx_img_wrap( | |
| 76 NULL, IMG_FMT_I420, cast_config_.width, cast_config_.height, 1, NULL); | |
| 77 | |
| 78 for (int i = 0; i < kNumberOfVp8VideoBuffers; ++i) { | |
| 79 acked_frame_buffers_[i] = true; | |
| 80 used_buffers_frame_id_[i] = kStartFrameId; | |
| 81 } | |
| 82 InitEncode(cast_config_.number_of_encode_threads); | |
| 83 } | |
| 84 | |
| 85 void Vp8Encoder::InitEncode(int number_of_encode_threads) { | |
| 86 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 87 // Populate encoder configuration with default values. | |
| 88 if (vpx_codec_enc_config_default(vpx_codec_vp8_cx(), config_.get(), 0)) { | |
| 89 DCHECK(false) << "Invalid return value"; | |
| 90 } | |
| 91 config_->g_w = cast_config_.width; | |
| 92 config_->g_h = cast_config_.height; | |
| 93 config_->rc_target_bitrate = cast_config_.start_bitrate / 1000; // In kbit/s. | |
| 94 | |
| 95 // Setting the codec time base. | |
| 96 config_->g_timebase.num = 1; | |
| 97 config_->g_timebase.den = kVideoFrequency; | |
| 98 config_->g_lag_in_frames = 0; | |
| 99 config_->kf_mode = VPX_KF_DISABLED; | |
| 100 if (use_multiple_video_buffers_) { | |
| 101 // We must enable error resilience when we use multiple buffers, due to | |
| 102 // codec requirements. | |
| 103 config_->g_error_resilient = 1; | |
| 104 } | |
| 105 config_->g_threads = number_of_encode_threads; | |
| 106 | |
| 107 // Rate control settings. | |
| 108 // Never allow the encoder to drop frame internally. | |
| 109 config_->rc_dropframe_thresh = 0; | |
| 110 config_->rc_end_usage = VPX_CBR; | |
| 111 config_->g_pass = VPX_RC_ONE_PASS; | |
| 112 config_->rc_resize_allowed = 0; | |
| 113 config_->rc_min_quantizer = cast_config_.min_qp; | |
| 114 config_->rc_max_quantizer = cast_config_.max_qp; | |
| 115 config_->rc_undershoot_pct = 100; | |
| 116 config_->rc_overshoot_pct = 15; | |
| 117 config_->rc_buf_initial_sz = 500; | |
| 118 config_->rc_buf_optimal_sz = 600; | |
| 119 config_->rc_buf_sz = 1000; | |
| 120 | |
| 121 // set the maximum target size of any key-frame. | |
| 122 uint32 rc_max_intra_target = MaxIntraTarget(config_->rc_buf_optimal_sz); | |
| 123 vpx_codec_flags_t flags = 0; | |
| 124 if (vpx_codec_enc_init( | |
| 125 encoder_.get(), vpx_codec_vp8_cx(), config_.get(), flags)) { | |
| 126 DCHECK(false) << "vpx_codec_enc_init() failed."; | |
| 127 encoder_.reset(); | |
| 128 return; | |
| 129 } | |
| 130 vpx_codec_control(encoder_.get(), VP8E_SET_STATIC_THRESHOLD, 1); | |
| 131 vpx_codec_control(encoder_.get(), VP8E_SET_NOISE_SENSITIVITY, 0); | |
| 132 vpx_codec_control(encoder_.get(), VP8E_SET_CPUUSED, -6); | |
| 133 vpx_codec_control( | |
| 134 encoder_.get(), VP8E_SET_MAX_INTRA_BITRATE_PCT, rc_max_intra_target); | |
| 135 } | |
| 136 | |
| 137 bool Vp8Encoder::Encode(const scoped_refptr<media::VideoFrame>& video_frame, | |
| 138 transport::EncodedFrame* encoded_image) { | |
| 139 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 140 // Image in vpx_image_t format. | |
| 141 // Input image is const. VP8's raw image is not defined as const. | |
| 142 raw_image_->planes[PLANE_Y] = | |
| 143 const_cast<uint8*>(video_frame->data(VideoFrame::kYPlane)); | |
| 144 raw_image_->planes[PLANE_U] = | |
| 145 const_cast<uint8*>(video_frame->data(VideoFrame::kUPlane)); | |
| 146 raw_image_->planes[PLANE_V] = | |
| 147 const_cast<uint8*>(video_frame->data(VideoFrame::kVPlane)); | |
| 148 | |
| 149 raw_image_->stride[VPX_PLANE_Y] = video_frame->stride(VideoFrame::kYPlane); | |
| 150 raw_image_->stride[VPX_PLANE_U] = video_frame->stride(VideoFrame::kUPlane); | |
| 151 raw_image_->stride[VPX_PLANE_V] = video_frame->stride(VideoFrame::kVPlane); | |
| 152 | |
| 153 uint8 latest_frame_id_to_reference; | |
| 154 Vp8Buffers buffer_to_update; | |
| 155 vpx_codec_flags_t flags = 0; | |
| 156 if (key_frame_requested_) { | |
| 157 flags = VPX_EFLAG_FORCE_KF; | |
| 158 // Self reference. | |
| 159 latest_frame_id_to_reference = last_encoded_frame_id_ + 1; | |
| 160 // We can pick any buffer as buffer_to_update since we update | |
| 161 // them all. | |
| 162 buffer_to_update = kLastBuffer; | |
| 163 } else { | |
| 164 // Reference all acked frames (buffers). | |
| 165 latest_frame_id_to_reference = GetLatestFrameIdToReference(); | |
| 166 GetCodecReferenceFlags(&flags); | |
| 167 buffer_to_update = GetNextBufferToUpdate(); | |
| 168 GetCodecUpdateFlags(buffer_to_update, &flags); | |
| 169 } | |
| 170 | |
| 171 // Note: The duration does not reflect the real time between frames. This is | |
| 172 // done to keep the encoder happy. | |
| 173 // | |
| 174 // TODO(miu): This is a semi-hack. We should consider using | |
| 175 // |video_frame->timestamp()| instead. | |
| 176 uint32 duration = kVideoFrequency / cast_config_.max_frame_rate; | |
| 177 | |
| 178 // Note: Timestamp here is used for bitrate calculation. The absolute value | |
| 179 // is not important. | |
| 180 if (!first_frame_received_) { | |
| 181 first_frame_received_ = true; | |
| 182 first_frame_timestamp_ = video_frame->timestamp(); | |
| 183 } | |
| 184 | |
| 185 vpx_codec_pts_t timestamp = | |
| 186 (video_frame->timestamp() - first_frame_timestamp_).InMicroseconds() * | |
| 187 kVideoFrequency / base::Time::kMicrosecondsPerSecond; | |
| 188 | |
| 189 if (vpx_codec_encode(encoder_.get(), | |
| 190 raw_image_, | |
| 191 timestamp, | |
| 192 duration, | |
| 193 flags, | |
| 194 VPX_DL_REALTIME) != VPX_CODEC_OK) { | |
| 195 LOG(ERROR) << "Failed to encode for once."; | |
| 196 return false; | |
| 197 } | |
| 198 | |
| 199 // Get encoded frame. | |
| 200 const vpx_codec_cx_pkt_t* pkt = NULL; | |
| 201 vpx_codec_iter_t iter = NULL; | |
| 202 bool is_key_frame = false; | |
| 203 while ((pkt = vpx_codec_get_cx_data(encoder_.get(), &iter)) != NULL) { | |
| 204 if (pkt->kind != VPX_CODEC_CX_FRAME_PKT) | |
| 205 continue; | |
| 206 encoded_image->data.assign( | |
| 207 static_cast<const uint8*>(pkt->data.frame.buf), | |
| 208 static_cast<const uint8*>(pkt->data.frame.buf) + pkt->data.frame.sz); | |
| 209 is_key_frame = !!(pkt->data.frame.flags & VPX_FRAME_IS_KEY); | |
| 210 break; // Done, since all data is provided in one CX_FRAME_PKT packet. | |
| 211 } | |
| 212 // Don't update frame_id for zero size frames. | |
| 213 if (encoded_image->data.empty()) | |
| 214 return true; | |
| 215 | |
| 216 // Populate the encoded frame. | |
| 217 encoded_image->frame_id = ++last_encoded_frame_id_; | |
| 218 if (is_key_frame) { | |
| 219 encoded_image->dependency = transport::EncodedFrame::KEY; | |
| 220 encoded_image->referenced_frame_id = encoded_image->frame_id; | |
| 221 } else { | |
| 222 encoded_image->dependency = transport::EncodedFrame::DEPENDENT; | |
| 223 encoded_image->referenced_frame_id = latest_frame_id_to_reference; | |
| 224 } | |
| 225 | |
| 226 DVLOG(1) << "VP8 encoded frame_id " << encoded_image->frame_id | |
| 227 << ", sized:" << encoded_image->data.size(); | |
| 228 | |
| 229 if (is_key_frame) { | |
| 230 key_frame_requested_ = false; | |
| 231 | |
| 232 for (int i = 0; i < kNumberOfVp8VideoBuffers; ++i) { | |
| 233 used_buffers_frame_id_[i] = encoded_image->frame_id; | |
| 234 } | |
| 235 // We can pick any buffer as last_used_vp8_buffer_ since we update | |
| 236 // them all. | |
| 237 last_used_vp8_buffer_ = buffer_to_update; | |
| 238 } else { | |
| 239 if (buffer_to_update != kNoBuffer) { | |
| 240 acked_frame_buffers_[buffer_to_update] = false; | |
| 241 used_buffers_frame_id_[buffer_to_update] = encoded_image->frame_id; | |
| 242 last_used_vp8_buffer_ = buffer_to_update; | |
| 243 } | |
| 244 } | |
| 245 return true; | |
| 246 } | |
| 247 | |
| 248 void Vp8Encoder::GetCodecReferenceFlags(vpx_codec_flags_t* flags) { | |
| 249 if (!use_multiple_video_buffers_) | |
| 250 return; | |
| 251 | |
| 252 // We need to reference something. | |
| 253 DCHECK(acked_frame_buffers_[kAltRefBuffer] || | |
| 254 acked_frame_buffers_[kGoldenBuffer] || | |
| 255 acked_frame_buffers_[kLastBuffer]) | |
| 256 << "Invalid state"; | |
| 257 | |
| 258 if (!acked_frame_buffers_[kAltRefBuffer]) { | |
| 259 *flags |= VP8_EFLAG_NO_REF_ARF; | |
| 260 } | |
| 261 if (!acked_frame_buffers_[kGoldenBuffer]) { | |
| 262 *flags |= VP8_EFLAG_NO_REF_GF; | |
| 263 } | |
| 264 if (!acked_frame_buffers_[kLastBuffer]) { | |
| 265 *flags |= VP8_EFLAG_NO_REF_LAST; | |
| 266 } | |
| 267 } | |
| 268 | |
| 269 uint32 Vp8Encoder::GetLatestFrameIdToReference() { | |
| 270 if (!use_multiple_video_buffers_) | |
| 271 return last_encoded_frame_id_; | |
| 272 | |
| 273 int latest_frame_id_to_reference = -1; | |
| 274 if (acked_frame_buffers_[kAltRefBuffer]) { | |
| 275 latest_frame_id_to_reference = used_buffers_frame_id_[kAltRefBuffer]; | |
| 276 } | |
| 277 if (acked_frame_buffers_[kGoldenBuffer]) { | |
| 278 if (latest_frame_id_to_reference == -1) { | |
| 279 latest_frame_id_to_reference = used_buffers_frame_id_[kGoldenBuffer]; | |
| 280 } else { | |
| 281 if (IsNewerFrameId(used_buffers_frame_id_[kGoldenBuffer], | |
| 282 latest_frame_id_to_reference)) { | |
| 283 latest_frame_id_to_reference = used_buffers_frame_id_[kGoldenBuffer]; | |
| 284 } | |
| 285 } | |
| 286 } | |
| 287 if (acked_frame_buffers_[kLastBuffer]) { | |
| 288 if (latest_frame_id_to_reference == -1) { | |
| 289 latest_frame_id_to_reference = used_buffers_frame_id_[kLastBuffer]; | |
| 290 } else { | |
| 291 if (IsNewerFrameId(used_buffers_frame_id_[kLastBuffer], | |
| 292 latest_frame_id_to_reference)) { | |
| 293 latest_frame_id_to_reference = used_buffers_frame_id_[kLastBuffer]; | |
| 294 } | |
| 295 } | |
| 296 } | |
| 297 DCHECK(latest_frame_id_to_reference != -1) << "Invalid state"; | |
| 298 return static_cast<uint32>(latest_frame_id_to_reference); | |
| 299 } | |
| 300 | |
| 301 Vp8Encoder::Vp8Buffers Vp8Encoder::GetNextBufferToUpdate() { | |
| 302 if (!use_multiple_video_buffers_) | |
| 303 return kNoBuffer; | |
| 304 | |
| 305 // Update at most one buffer, except for key-frames. | |
| 306 | |
| 307 Vp8Buffers buffer_to_update = kNoBuffer; | |
| 308 if (number_of_repeated_buffers_ < max_number_of_repeated_buffers_in_a_row_) { | |
| 309 // TODO(pwestin): experiment with this. The issue with only this change is | |
| 310 // that we can end up with only 4 frames in flight when we expect 6. | |
| 311 // buffer_to_update = last_used_vp8_buffer_; | |
| 312 buffer_to_update = kNoBuffer; | |
| 313 ++number_of_repeated_buffers_; | |
| 314 } else { | |
| 315 number_of_repeated_buffers_ = 0; | |
| 316 switch (last_used_vp8_buffer_) { | |
| 317 case kAltRefBuffer: | |
| 318 buffer_to_update = kLastBuffer; | |
| 319 VLOG(1) << "VP8 update last buffer"; | |
| 320 break; | |
| 321 case kLastBuffer: | |
| 322 buffer_to_update = kGoldenBuffer; | |
| 323 VLOG(1) << "VP8 update golden buffer"; | |
| 324 break; | |
| 325 case kGoldenBuffer: | |
| 326 buffer_to_update = kAltRefBuffer; | |
| 327 VLOG(1) << "VP8 update alt-ref buffer"; | |
| 328 break; | |
| 329 case kNoBuffer: | |
| 330 DCHECK(false) << "Invalid state"; | |
| 331 break; | |
| 332 } | |
| 333 } | |
| 334 return buffer_to_update; | |
| 335 } | |
| 336 | |
| 337 void Vp8Encoder::GetCodecUpdateFlags(Vp8Buffers buffer_to_update, | |
| 338 vpx_codec_flags_t* flags) { | |
| 339 if (!use_multiple_video_buffers_) | |
| 340 return; | |
| 341 | |
| 342 // Update at most one buffer, except for key-frames. | |
| 343 switch (buffer_to_update) { | |
| 344 case kAltRefBuffer: | |
| 345 *flags |= VP8_EFLAG_NO_UPD_GF; | |
| 346 *flags |= VP8_EFLAG_NO_UPD_LAST; | |
| 347 break; | |
| 348 case kLastBuffer: | |
| 349 *flags |= VP8_EFLAG_NO_UPD_GF; | |
| 350 *flags |= VP8_EFLAG_NO_UPD_ARF; | |
| 351 break; | |
| 352 case kGoldenBuffer: | |
| 353 *flags |= VP8_EFLAG_NO_UPD_ARF; | |
| 354 *flags |= VP8_EFLAG_NO_UPD_LAST; | |
| 355 break; | |
| 356 case kNoBuffer: | |
| 357 *flags |= VP8_EFLAG_NO_UPD_ARF; | |
| 358 *flags |= VP8_EFLAG_NO_UPD_GF; | |
| 359 *flags |= VP8_EFLAG_NO_UPD_LAST; | |
| 360 *flags |= VP8_EFLAG_NO_UPD_ENTROPY; | |
| 361 break; | |
| 362 } | |
| 363 } | |
| 364 | |
| 365 void Vp8Encoder::UpdateRates(uint32 new_bitrate) { | |
| 366 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 367 uint32 new_bitrate_kbit = new_bitrate / 1000; | |
| 368 if (config_->rc_target_bitrate == new_bitrate_kbit) | |
| 369 return; | |
| 370 | |
| 371 config_->rc_target_bitrate = new_bitrate_kbit; | |
| 372 | |
| 373 // Update encoder context. | |
| 374 if (vpx_codec_enc_config_set(encoder_.get(), config_.get())) { | |
| 375 DCHECK(false) << "Invalid return value"; | |
| 376 } | |
| 377 } | |
| 378 | |
| 379 void Vp8Encoder::LatestFrameIdToReference(uint32 frame_id) { | |
| 380 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 381 if (!use_multiple_video_buffers_) | |
| 382 return; | |
| 383 | |
| 384 VLOG(1) << "VP8 ok to reference frame:" << static_cast<int>(frame_id); | |
| 385 for (int i = 0; i < kNumberOfVp8VideoBuffers; ++i) { | |
| 386 if (frame_id == used_buffers_frame_id_[i]) { | |
| 387 acked_frame_buffers_[i] = true; | |
| 388 } | |
| 389 } | |
| 390 } | |
| 391 | |
| 392 void Vp8Encoder::GenerateKeyFrame() { | |
| 393 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 394 key_frame_requested_ = true; | |
| 395 } | |
| 396 | |
| 397 // Calculate the max size of the key frame relative to a normal delta frame. | |
| 398 uint32 Vp8Encoder::MaxIntraTarget(uint32 optimal_buffer_size_ms) const { | |
| 399 // Set max to the optimal buffer level (normalized by target BR), | |
| 400 // and scaled by a scale_parameter. | |
| 401 // Max target size = scalePar * optimalBufferSize * targetBR[Kbps]. | |
| 402 // This values is presented in percentage of perFrameBw: | |
| 403 // perFrameBw = targetBR[Kbps] * 1000 / frameRate. | |
| 404 // The target in % is as follows: | |
| 405 | |
| 406 float scale_parameter = 0.5; | |
| 407 uint32 target_pct = optimal_buffer_size_ms * scale_parameter * | |
| 408 cast_config_.max_frame_rate / 10; | |
| 409 | |
| 410 // Don't go below 3 times the per frame bandwidth. | |
| 411 return std::max(target_pct, kMinIntra); | |
| 412 } | |
| 413 | |
| 414 } // namespace cast | |
| 415 } // namespace media | |
| OLD | NEW |