Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(350)

Side by Side Diff: remoting/codec/video_encoder_vpx.cc

Issue 1908203002: Adapt encoder behavior to target bitrate (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 4 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « remoting/codec/video_encoder_vpx.h ('k') | remoting/proto/video.proto » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2013 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "remoting/codec/video_encoder_vpx.h" 5 #include "remoting/codec/video_encoder_vpx.h"
6 6
7 #include <utility> 7 #include <utility>
8 8
9 #include "base/bind.h" 9 #include "base/bind.h"
10 #include "base/logging.h" 10 #include "base/logging.h"
(...skipping 24 matching lines...) Expand all
35 const int kMacroBlockSize = 16; 35 const int kMacroBlockSize = 16;
36 36
37 // Magic encoder profile numbers for I420 and I444 input formats. 37 // Magic encoder profile numbers for I420 and I444 input formats.
38 const int kVp9I420ProfileNumber = 0; 38 const int kVp9I420ProfileNumber = 0;
39 const int kVp9I444ProfileNumber = 1; 39 const int kVp9I444ProfileNumber = 1;
40 40
41 // Magic encoder constants for adaptive quantization strategy. 41 // Magic encoder constants for adaptive quantization strategy.
42 const int kVp9AqModeNone = 0; 42 const int kVp9AqModeNone = 0;
43 const int kVp9AqModeCyclicRefresh = 3; 43 const int kVp9AqModeCyclicRefresh = 3;
44 44
45 const int kDefaultTargetBitrateKbps = 1000;
46
45 void SetCommonCodecParameters(vpx_codec_enc_cfg_t* config, 47 void SetCommonCodecParameters(vpx_codec_enc_cfg_t* config,
46 const webrtc::DesktopSize& size) { 48 const webrtc::DesktopSize& size) {
47 // Use millisecond granularity time base. 49 // Use millisecond granularity time base.
48 config->g_timebase.num = 1; 50 config->g_timebase.num = 1;
49 config->g_timebase.den = 1000; 51 config->g_timebase.den = base::Time::kMicrosecondsPerSecond;
50 52
51 config->g_w = size.width(); 53 config->g_w = size.width();
52 config->g_h = size.height(); 54 config->g_h = size.height();
53 config->g_pass = VPX_RC_ONE_PASS; 55 config->g_pass = VPX_RC_ONE_PASS;
54 56
55 // Start emitting packets immediately. 57 // Start emitting packets immediately.
56 config->g_lag_in_frames = 0; 58 config->g_lag_in_frames = 0;
57 59
58 // Since the transport layer is reliable, keyframes should not be necessary. 60 // Since the transport layer is reliable, keyframes should not be necessary.
59 // However, due to crbug.com/440223, decoding fails after 30,000 non-key 61 // However, due to crbug.com/440223, decoding fails after 30,000 non-key
60 // frames, so take the hit of an "unnecessary" key-frame every 10,000 frames. 62 // frames, so take the hit of an "unnecessary" key-frame every 10,000 frames.
61 config->kf_min_dist = 10000; 63 config->kf_min_dist = 10000;
62 config->kf_max_dist = 10000; 64 config->kf_max_dist = 10000;
63 65
64 // Using 2 threads gives a great boost in performance for most systems with 66 // Allow multiple cores on a system to be used for encoding for
65 // adequate processing power. NB: Going to multiple threads on low end 67 // performance while at the same time ensuring we do not saturate.
66 // windows systems can really hurt performance. 68 config->g_threads = (base::SysInfo::NumberOfProcessors() + 1) / 2;
67 // http://crbug.com/99179 69
68 config->g_threads = (base::SysInfo::NumberOfProcessors() > 2) ? 2 : 1; 70 // Do not drop any frames at encoder.
71 config->rc_dropframe_thresh = 0;
72 // We do not want variations in bandwidth.
73 config->rc_end_usage = VPX_CBR;
74 config->rc_undershoot_pct = 100;
75 config->rc_overshoot_pct = 15;
69 } 76 }
70 77
71 void SetVp8CodecParameters(vpx_codec_enc_cfg_t* config, 78 void SetVp8CodecParameters(vpx_codec_enc_cfg_t* config,
72 const webrtc::DesktopSize& size) { 79 const webrtc::DesktopSize& size) {
73 // Adjust default target bit-rate to account for actual desktop size.
74 config->rc_target_bitrate = size.width() * size.height() *
75 config->rc_target_bitrate / config->g_w / config->g_h;
76
77 SetCommonCodecParameters(config, size); 80 SetCommonCodecParameters(config, size);
78 81
79 // Value of 2 means using the real time profile. This is basically a 82 // Value of 2 means using the real time profile. This is basically a
80 // redundant option since we explicitly select real time mode when doing 83 // redundant option since we explicitly select real time mode when doing
81 // encoding. 84 // encoding.
82 config->g_profile = 2; 85 config->g_profile = 2;
83 86
84 // Clamping the quantizer constrains the worst-case quality and CPU usage. 87 // To enable remoting to be highly interactive and allow the target bitrate
88 // to be met, we relax the max quantizer. The quality will get topped-off
89 // in subsequent frames.
85 config->rc_min_quantizer = 20; 90 config->rc_min_quantizer = 20;
86 config->rc_max_quantizer = 30; 91 config->rc_max_quantizer = 63;
87 } 92 }
88 93
89 void SetVp9CodecParameters(vpx_codec_enc_cfg_t* config, 94 void SetVp9CodecParameters(vpx_codec_enc_cfg_t* config,
90 const webrtc::DesktopSize& size, 95 const webrtc::DesktopSize& size,
91 bool lossless_color, 96 bool lossless_color,
92 bool lossless_encode) { 97 bool lossless_encode) {
93 SetCommonCodecParameters(config, size); 98 SetCommonCodecParameters(config, size);
94 99
95 // Configure VP9 for I420 or I444 source frames. 100 // Configure VP9 for I420 or I444 source frames.
96 config->g_profile = 101 config->g_profile =
(...skipping 164 matching lines...) Expand 10 before | Expand all | Expand 10 after
261 lossless_color_ = want_lossless; 266 lossless_color_ = want_lossless;
262 // TODO(wez): Switch to ConfigureCodec() path once libvpx supports it. 267 // TODO(wez): Switch to ConfigureCodec() path once libvpx supports it.
263 // See https://code.google.com/p/webm/issues/detail?id=913. 268 // See https://code.google.com/p/webm/issues/detail?id=913.
264 // if (codec_) 269 // if (codec_)
265 // Configure(webrtc::DesktopSize(codec_->config.enc->g_w, 270 // Configure(webrtc::DesktopSize(codec_->config.enc->g_w,
266 // codec_->config.enc->g_h)); 271 // codec_->config.enc->g_h));
267 codec_.reset(); 272 codec_.reset();
268 } 273 }
269 } 274 }
270 275
276 void VideoEncoderVpx::UpdateTargetBitrate(uint32_t new_bitrate) {
277 target_bitrate_kbps_ = new_bitrate;
278 // Configuration not initialized.
279 if (config_.g_timebase.den == 0)
280 return;
281
282 if (config_.rc_target_bitrate == new_bitrate)
283 return;
284 config_.rc_target_bitrate = new_bitrate;
285
286 // Update encoder context.
287 if (vpx_codec_enc_config_set(codec_.get(), &config_))
288 NOTREACHED() << "Unable to set encoder config";
289
290 VLOG(1) << "New rc_target_bitrate: " << new_bitrate << " kbps";
291 }
292
271 std::unique_ptr<VideoPacket> VideoEncoderVpx::Encode( 293 std::unique_ptr<VideoPacket> VideoEncoderVpx::Encode(
272 const webrtc::DesktopFrame& frame, 294 const webrtc::DesktopFrame& frame,
273 uint32_t flags) { 295 uint32_t flags) {
274 DCHECK_LE(32, frame.size().width()); 296 DCHECK_LE(32, frame.size().width());
275 DCHECK_LE(32, frame.size().height()); 297 DCHECK_LE(32, frame.size().height());
276 298
277 // If there is nothing to encode, and nothing to top-off, then return nothing. 299 // VP8: Encode top-off is controlled at the call site.
278 if (frame.updated_region().is_empty() && !encode_unchanged_frame_) 300 // VP9: Based on information fetching active map, we return here if there is
301 // nothing
302 // to top-off.
Sergey Ulanov 2016/04/21 22:55:49 nit: comment formatting
303 if (use_vp9_ && frame.updated_region().is_empty() && !encode_unchanged_frame_)
279 return nullptr; 304 return nullptr;
280 305
281 // Create or reconfigure the codec to match the size of |frame|. 306 // Create or reconfigure the codec to match the size of |frame|.
282 if (!codec_ || 307 if (!codec_ ||
283 (image_ && 308 (image_ &&
284 !frame.size().equals(webrtc::DesktopSize(image_->w, image_->h)))) { 309 !frame.size().equals(webrtc::DesktopSize(image_->w, image_->h)))) {
285 Configure(frame.size()); 310 Configure(frame.size());
286 } 311 }
287 312
288 // Convert the updated capture data ready for encode.
289 webrtc::DesktopRegion updated_region;
290 PrepareImage(frame, &updated_region);
291
292 // Update active map based on updated region.
293 SetActiveMapFromRegion(updated_region);
294
295 // Apply active map to the encoder.
296 vpx_active_map_t act_map; 313 vpx_active_map_t act_map;
297 act_map.rows = active_map_size_.height(); 314 act_map.rows = active_map_size_.height();
298 act_map.cols = active_map_size_.width(); 315 act_map.cols = active_map_size_.width();
299 act_map.active_map = active_map_.get(); 316 act_map.active_map = active_map_.get();
300 if (vpx_codec_control(codec_.get(), VP8E_SET_ACTIVEMAP, &act_map)) { 317
301 LOG(ERROR) << "Unable to apply active map"; 318 webrtc::DesktopRegion updated_region;
319 if (!frame.updated_region().is_empty()) {
320 // Convert the updated capture data ready for encode.
321 PrepareImage(frame, &updated_region);
322
323 // Update active map based on updated region.
324 SetActiveMapFromRegion(updated_region);
325
326 // Apply active map to the encoder.
327
328 if (vpx_codec_control(codec_.get(), VP8E_SET_ACTIVEMAP, &act_map)) {
329 LOG(ERROR) << "Unable to apply active map";
330 }
302 } 331 }
303 332
304 if (flags & REQUEST_KEY_FRAME) 333 // Frame rate is adapted based on how well the encoder meets the target
305 vpx_codec_control(codec_.get(), VP8E_SET_FRAME_FLAGS, VPX_EFLAG_FORCE_KF); 334 // bandwidth requiremetn. We specify a target rate of 1 / 15 fps here.
306
307 // Do the actual encoding.
308 int timestamp = (clock_->NowTicks() - timestamp_base_).InMilliseconds();
309 vpx_codec_err_t ret = vpx_codec_encode( 335 vpx_codec_err_t ret = vpx_codec_encode(
310 codec_.get(), image_.get(), timestamp, 1, 0, VPX_DL_REALTIME); 336 codec_.get(), image_.get(), 0, 66666,
337 (flags & REQUEST_KEY_FRAME) ? VPX_EFLAG_FORCE_KF : 0, VPX_DL_REALTIME);
311 DCHECK_EQ(ret, VPX_CODEC_OK) 338 DCHECK_EQ(ret, VPX_CODEC_OK)
312 << "Encoding error: " << vpx_codec_err_to_string(ret) << "\n" 339 << "Encoding error: " << vpx_codec_err_to_string(ret) << "\n"
313 << "Details: " << vpx_codec_error(codec_.get()) << "\n" 340 << "Details: " << vpx_codec_error(codec_.get()) << "\n"
314 << vpx_codec_error_detail(codec_.get()); 341 << vpx_codec_error_detail(codec_.get());
315 342
316 if (use_vp9_ && !lossless_encode_) { 343 if (use_vp9_ && !lossless_encode_) {
317 ret = vpx_codec_control(codec_.get(), VP9E_GET_ACTIVEMAP, &act_map); 344 ret = vpx_codec_control(codec_.get(), VP9E_GET_ACTIVEMAP, &act_map);
318 DCHECK_EQ(ret, VPX_CODEC_OK) 345 DCHECK_EQ(ret, VPX_CODEC_OK)
319 << "Failed to fetch active map: " 346 << "Failed to fetch active map: "
320 << vpx_codec_err_to_string(ret) << "\n"; 347 << vpx_codec_err_to_string(ret) << "\n";
(...skipping 12 matching lines...) Expand all
333 std::unique_ptr<VideoPacket> packet( 360 std::unique_ptr<VideoPacket> packet(
334 helper_.CreateVideoPacketWithUpdatedRegion(frame, updated_region)); 361 helper_.CreateVideoPacketWithUpdatedRegion(frame, updated_region));
335 packet->mutable_format()->set_encoding(VideoPacketFormat::ENCODING_VP8); 362 packet->mutable_format()->set_encoding(VideoPacketFormat::ENCODING_VP8);
336 363
337 while (!got_data) { 364 while (!got_data) {
338 const vpx_codec_cx_pkt_t* vpx_packet = 365 const vpx_codec_cx_pkt_t* vpx_packet =
339 vpx_codec_get_cx_data(codec_.get(), &iter); 366 vpx_codec_get_cx_data(codec_.get(), &iter);
340 if (!vpx_packet) 367 if (!vpx_packet)
341 continue; 368 continue;
342 369
370 int quantizer = -1;
343 switch (vpx_packet->kind) { 371 switch (vpx_packet->kind) {
344 case VPX_CODEC_CX_FRAME_PKT: 372 case VPX_CODEC_CX_FRAME_PKT:
345 got_data = true; 373 got_data = true;
346 packet->set_data(vpx_packet->data.frame.buf, vpx_packet->data.frame.sz); 374 packet->set_data(vpx_packet->data.frame.buf, vpx_packet->data.frame.sz);
347 packet->set_key_frame(vpx_packet->data.frame.flags & VPX_FRAME_IS_KEY); 375 packet->set_key_frame(vpx_packet->data.frame.flags & VPX_FRAME_IS_KEY);
376 CHECK_EQ(vpx_codec_control(codec_.get(), VP8E_GET_LAST_QUANTIZER_64,
377 &quantizer),
378 VPX_CODEC_OK);
379 packet->set_quantizer(quantizer);
348 break; 380 break;
349 default: 381 default:
350 break; 382 break;
351 } 383 }
352 } 384 }
353 385
354 return packet; 386 return packet;
355 } 387 }
356 388
357 VideoEncoderVpx::VideoEncoderVpx(bool use_vp9) 389 VideoEncoderVpx::VideoEncoderVpx(bool use_vp9)
358 : use_vp9_(use_vp9), 390 : use_vp9_(use_vp9),
391 target_bitrate_kbps_(kDefaultTargetBitrateKbps),
359 encode_unchanged_frame_(false), 392 encode_unchanged_frame_(false),
360 clock_(&default_tick_clock_) {} 393 clock_(&default_tick_clock_) {
394 // Indicates config is still uninitialized.
395 config_.g_timebase.den = 0;
396 }
361 397
362 void VideoEncoderVpx::Configure(const webrtc::DesktopSize& size) { 398 void VideoEncoderVpx::Configure(const webrtc::DesktopSize& size) {
363 DCHECK(use_vp9_ || !lossless_color_); 399 DCHECK(use_vp9_ || !lossless_color_);
364 DCHECK(use_vp9_ || !lossless_encode_); 400 DCHECK(use_vp9_ || !lossless_encode_);
365 401
366 // Tear down |image_| if it no longer matches the size and color settings. 402 // Tear down |image_| if it no longer matches the size and color settings.
367 // PrepareImage() will then create a new buffer of the required dimensions if 403 // PrepareImage() will then create a new buffer of the required dimensions if
368 // |image_| is not allocated. 404 // |image_| is not allocated.
369 FreeImageIfMismatched(lossless_color_, size, &image_, &image_buffer_); 405 FreeImageIfMismatched(lossless_color_, size, &image_, &image_buffer_);
370 406
371 // Initialize active map. 407 // Initialize active map.
372 active_map_size_ = webrtc::DesktopSize( 408 active_map_size_ = webrtc::DesktopSize(
373 (size.width() + kMacroBlockSize - 1) / kMacroBlockSize, 409 (size.width() + kMacroBlockSize - 1) / kMacroBlockSize,
374 (size.height() + kMacroBlockSize - 1) / kMacroBlockSize); 410 (size.height() + kMacroBlockSize - 1) / kMacroBlockSize);
375 active_map_.reset( 411 active_map_.reset(
376 new uint8_t[active_map_size_.width() * active_map_size_.height()]); 412 new uint8_t[active_map_size_.width() * active_map_size_.height()]);
377 413
378 // TODO(wez): Remove this hack once VPX can handle frame size reconfiguration. 414 // TODO(wez): Remove this hack once VPX can handle frame size reconfiguration.
379 // See https://code.google.com/p/webm/issues/detail?id=912. 415 // See https://code.google.com/p/webm/issues/detail?id=912.
380 if (codec_) { 416 if (codec_) {
381 // If the frame size has changed then force re-creation of the codec. 417 // If the frame size has changed then force re-creation of the codec.
382 if (codec_->config.enc->g_w != static_cast<unsigned int>(size.width()) || 418 if (codec_->config.enc->g_w != static_cast<unsigned int>(size.width()) ||
383 codec_->config.enc->g_h != static_cast<unsigned int>(size.height())) { 419 codec_->config.enc->g_h != static_cast<unsigned int>(size.height())) {
384 codec_.reset(); 420 codec_.reset();
385 } 421 }
386 } 422 }
387 423
388 // (Re)Set the base for frame timestamps if the codec is being (re)created.
389 if (!codec_) {
390 timestamp_base_ = clock_->NowTicks();
391 }
392
393 // Fetch a default configuration for the desired codec. 424 // Fetch a default configuration for the desired codec.
394 const vpx_codec_iface_t* interface = 425 const vpx_codec_iface_t* interface =
395 use_vp9_ ? vpx_codec_vp9_cx() : vpx_codec_vp8_cx(); 426 use_vp9_ ? vpx_codec_vp9_cx() : vpx_codec_vp8_cx();
396 vpx_codec_enc_cfg_t config; 427 vpx_codec_err_t ret = vpx_codec_enc_config_default(interface, &config_, 0);
397 vpx_codec_err_t ret = vpx_codec_enc_config_default(interface, &config, 0);
398 DCHECK_EQ(VPX_CODEC_OK, ret) << "Failed to fetch default configuration"; 428 DCHECK_EQ(VPX_CODEC_OK, ret) << "Failed to fetch default configuration";
399 429
400 // Customize the default configuration to our needs. 430 // Customize the default configuration to our needs.
401 if (use_vp9_) { 431 if (use_vp9_) {
402 SetVp9CodecParameters(&config, size, lossless_color_, lossless_encode_); 432 SetVp9CodecParameters(&config_, size, lossless_color_, lossless_encode_);
403 } else { 433 } else {
404 SetVp8CodecParameters(&config, size); 434 SetVp8CodecParameters(&config_, size);
405 } 435 }
436 config_.rc_target_bitrate = target_bitrate_kbps_;
406 437
407 // Initialize or re-configure the codec with the custom configuration. 438 // Initialize or re-configure the codec with the custom configuration.
408 if (!codec_) { 439 if (!codec_) {
409 codec_.reset(new vpx_codec_ctx_t); 440 codec_.reset(new vpx_codec_ctx_t);
410 ret = vpx_codec_enc_init(codec_.get(), interface, &config, 0); 441 ret = vpx_codec_enc_init(codec_.get(), interface, &config_, 0);
411 CHECK_EQ(VPX_CODEC_OK, ret) << "Failed to initialize codec"; 442 CHECK_EQ(VPX_CODEC_OK, ret) << "Failed to initialize codec";
412 } else { 443 } else {
413 ret = vpx_codec_enc_config_set(codec_.get(), &config); 444 ret = vpx_codec_enc_config_set(codec_.get(), &config_);
414 CHECK_EQ(VPX_CODEC_OK, ret) << "Failed to reconfigure codec"; 445 CHECK_EQ(VPX_CODEC_OK, ret) << "Failed to reconfigure codec";
415 } 446 }
416 447
417 // Apply further customizations to the codec now it's initialized. 448 // Apply further customizations to the codec now it's initialized.
418 if (use_vp9_) { 449 if (use_vp9_) {
419 SetVp9CodecOptions(codec_.get(), lossless_encode_); 450 SetVp9CodecOptions(codec_.get(), lossless_encode_);
420 } else { 451 } else {
421 SetVp8CodecOptions(codec_.get()); 452 SetVp8CodecOptions(codec_.get());
422 } 453 }
423 } 454 }
(...skipping 123 matching lines...) Expand 10 before | Expand all | Expand 10 after
547 kMacroBlockSize * (y + 1))); 578 kMacroBlockSize * (y + 1)));
548 } 579 }
549 x0 = x1 + 1; 580 x0 = x1 + 1;
550 } 581 }
551 } 582 }
552 updated_region->IntersectWith( 583 updated_region->IntersectWith(
553 webrtc::DesktopRect::MakeWH(image_->w, image_->h)); 584 webrtc::DesktopRect::MakeWH(image_->w, image_->h));
554 } 585 }
555 586
556 } // namespace remoting 587 } // namespace remoting
OLDNEW
« no previous file with comments | « remoting/codec/video_encoder_vpx.h ('k') | remoting/proto/video.proto » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698