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

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

Issue 2335923002: Add WebrtcCaptureScheduler interface. (Closed)
Patch Set: Created 4 years, 3 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
OLDNEW
1 // Copyright 2016 The Chromium Authors. All rights reserved. 1 // Copyright 2016 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/webrtc_video_encoder_vpx.h" 5 #include "remoting/codec/webrtc_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 26 matching lines...) Expand all
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; 45 const int kDefaultTargetBitrateKbps = 1000;
46 46
47 // Target quantizer at which stop the encoding top-off.
48 const int kTargetQuantizerForVp8TopOff = 30;
49
50 void SetCommonCodecParameters(vpx_codec_enc_cfg_t* config, 47 void SetCommonCodecParameters(vpx_codec_enc_cfg_t* config,
51 const webrtc::DesktopSize& size) { 48 const webrtc::DesktopSize& size) {
52 // Use millisecond granularity time base. 49 // Use millisecond granularity time base.
53 config->g_timebase.num = 1; 50 config->g_timebase.num = 1;
54 config->g_timebase.den = base::Time::kMicrosecondsPerSecond; 51 config->g_timebase.den = base::Time::kMicrosecondsPerSecond;
55 52
56 config->g_w = size.width(); 53 config->g_w = size.width();
57 config->g_h = size.height(); 54 config->g_h = size.height();
58 config->g_pass = VPX_RC_ONE_PASS; 55 config->g_pass = VPX_RC_ONE_PASS;
59 56
(...skipping 215 matching lines...) Expand 10 before | Expand all | Expand 10 after
275 codec_.reset(); 272 codec_.reset();
276 } 273 }
277 } 274 }
278 275
279 std::unique_ptr<WebrtcVideoEncoder::EncodedFrame> WebrtcVideoEncoderVpx::Encode( 276 std::unique_ptr<WebrtcVideoEncoder::EncodedFrame> WebrtcVideoEncoderVpx::Encode(
280 const webrtc::DesktopFrame& frame, 277 const webrtc::DesktopFrame& frame,
281 const FrameParams& params) { 278 const FrameParams& params) {
282 DCHECK_LE(32, frame.size().width()); 279 DCHECK_LE(32, frame.size().width());
283 DCHECK_LE(32, frame.size().height()); 280 DCHECK_LE(32, frame.size().height());
284 281
285 // Based on information fetching active map, we return here if there is
286 // nothing to top-off.
287 if (frame.updated_region().is_empty() && !encode_unchanged_frame_ &&
288 !params.key_frame) {
289 return nullptr;
290 }
291
292 // Create or reconfigure the codec to match the size of |frame|. 282 // Create or reconfigure the codec to match the size of |frame|.
293 if (!codec_ || 283 if (!codec_ ||
294 (image_ && 284 (image_ &&
295 !frame.size().equals(webrtc::DesktopSize(image_->w, image_->h)))) { 285 !frame.size().equals(webrtc::DesktopSize(image_->w, image_->h)))) {
296 Configure(frame.size()); 286 Configure(frame.size());
297 } 287 }
298 288
299 UpdateTargetBitrate(params.bitrate_kbps); 289 UpdateConfig(params);
300 290
301 vpx_active_map_t act_map; 291 vpx_active_map_t act_map;
302 act_map.rows = active_map_size_.height(); 292 act_map.rows = active_map_size_.height();
303 act_map.cols = active_map_size_.width(); 293 act_map.cols = active_map_size_.width();
304 act_map.active_map = active_map_.get(); 294 act_map.active_map = active_map_.get();
305 295
306 webrtc::DesktopRegion updated_region; 296 webrtc::DesktopRegion updated_region;
307 if (!frame.updated_region().is_empty()) { 297 // Convert the updated capture data ready for encode.
308 // Convert the updated capture data ready for encode. 298 PrepareImage(frame, &updated_region);
309 PrepareImage(frame, &updated_region);
310 299
311 // Update active map based on updated region. 300 // Update active map based on updated region.
312 SetActiveMapFromRegion(updated_region); 301 if (params.clear_active_map)
302 ClearActiveMap();
303 SetActiveMapFromRegion(updated_region);
313 304
314 // Apply active map to the encoder. 305 // Apply active map to the encoder.
315 306 if (vpx_codec_control(codec_.get(), VP8E_SET_ACTIVEMAP, &act_map)) {
316 if (vpx_codec_control(codec_.get(), VP8E_SET_ACTIVEMAP, &act_map)) { 307 LOG(ERROR) << "Unable to apply active map";
317 LOG(ERROR) << "Unable to apply active map";
318 }
319 } 308 }
320 309
321 vpx_codec_err_t ret = vpx_codec_encode( 310 vpx_codec_err_t ret = vpx_codec_encode(
322 codec_.get(), image_.get(), 0, params.duration.InMicroseconds(), 311 codec_.get(), image_.get(), 0, params.duration.InMicroseconds(),
323 (params.key_frame) ? VPX_EFLAG_FORCE_KF : 0, VPX_DL_REALTIME); 312 (params.key_frame) ? VPX_EFLAG_FORCE_KF : 0, VPX_DL_REALTIME);
324 DCHECK_EQ(ret, VPX_CODEC_OK) 313 DCHECK_EQ(ret, VPX_CODEC_OK)
325 << "Encoding error: " << vpx_codec_err_to_string(ret) << "\n" 314 << "Encoding error: " << vpx_codec_err_to_string(ret) << "\n"
326 << "Details: " << vpx_codec_error(codec_.get()) << "\n" 315 << "Details: " << vpx_codec_error(codec_.get()) << "\n"
327 << vpx_codec_error_detail(codec_.get()); 316 << vpx_codec_error_detail(codec_.get());
328 317
329 if (!lossless_encode_) { 318 if (!lossless_encode_) {
330 // VP8 doesn't return active map, so we assume it's the same on the output 319 // VP8 doesn't return active map, so we assume it's the same on the output
331 // as on the input. 320 // as on the input.
332 if (use_vp9_) { 321 if (use_vp9_) {
333 ret = vpx_codec_control(codec_.get(), VP9E_GET_ACTIVEMAP, &act_map); 322 ret = vpx_codec_control(codec_.get(), VP9E_GET_ACTIVEMAP, &act_map);
334 DCHECK_EQ(ret, VPX_CODEC_OK) 323 DCHECK_EQ(ret, VPX_CODEC_OK)
335 << "Failed to fetch active map: " << vpx_codec_err_to_string(ret) 324 << "Failed to fetch active map: " << vpx_codec_err_to_string(ret)
336 << "\n"; 325 << "\n";
337
338 // If the encoder output no changes then there's nothing left to top-off.
339 encode_unchanged_frame_ = !updated_region.is_empty();
340 } else {
341 // Always set |encode_unchanged_frame_| when using VP8. It will be reset
342 // below once the target quantizer value is reached.
343 encode_unchanged_frame_ = true;
344 } 326 }
345 327
346 UpdateRegionFromActiveMap(&updated_region); 328 UpdateRegionFromActiveMap(&updated_region);
347 } 329 }
348 330
349 // Read the encoded data. 331 // Read the encoded data.
350 vpx_codec_iter_t iter = nullptr; 332 vpx_codec_iter_t iter = nullptr;
351 bool got_data = false; 333 bool got_data = false;
352 334
353 std::unique_ptr<EncodedFrame> encoded_frame(new EncodedFrame()); 335 std::unique_ptr<EncodedFrame> encoded_frame(new EncodedFrame());
354 encoded_frame->size = frame.size(); 336 encoded_frame->size = frame.size();
355 337
356 while (!got_data) { 338 while (!got_data) {
357 const vpx_codec_cx_pkt_t* vpx_packet = 339 const vpx_codec_cx_pkt_t* vpx_packet =
358 vpx_codec_get_cx_data(codec_.get(), &iter); 340 vpx_codec_get_cx_data(codec_.get(), &iter);
359 if (!vpx_packet) 341 if (!vpx_packet)
360 continue; 342 continue;
361 343
362 switch (vpx_packet->kind) { 344 switch (vpx_packet->kind) {
363 case VPX_CODEC_CX_FRAME_PKT: { 345 case VPX_CODEC_CX_FRAME_PKT: {
364 got_data = true; 346 got_data = true;
365 // TODO(sergeyu): Avoid copying the data here.. 347 // TODO(sergeyu): Avoid copying the data here..
366 encoded_frame->data.assign( 348 encoded_frame->data.assign(
367 reinterpret_cast<const char*>(vpx_packet->data.frame.buf), 349 reinterpret_cast<const char*>(vpx_packet->data.frame.buf),
368 vpx_packet->data.frame.sz); 350 vpx_packet->data.frame.sz);
369 encoded_frame->key_frame = 351 encoded_frame->key_frame =
370 vpx_packet->data.frame.flags & VPX_FRAME_IS_KEY; 352 vpx_packet->data.frame.flags & VPX_FRAME_IS_KEY;
371 int quantizer = -1;
372 CHECK_EQ(vpx_codec_control(codec_.get(), VP8E_GET_LAST_QUANTIZER_64, 353 CHECK_EQ(vpx_codec_control(codec_.get(), VP8E_GET_LAST_QUANTIZER_64,
373 &quantizer), 354 &(encoded_frame->quantizer)),
374 VPX_CODEC_OK); 355 VPX_CODEC_OK);
375 // VP8: Stop top-off as soon as the target quantizer value is reached.
376 if (!use_vp9_ && quantizer <= kTargetQuantizerForVp8TopOff)
377 encode_unchanged_frame_ = false;
378 break; 356 break;
379 } 357 }
380 default: 358 default:
381 break; 359 break;
382 } 360 }
383 } 361 }
384 362
385 return encoded_frame; 363 return encoded_frame;
386 } 364 }
387 365
388 WebrtcVideoEncoderVpx::WebrtcVideoEncoderVpx(bool use_vp9) 366 WebrtcVideoEncoderVpx::WebrtcVideoEncoderVpx(bool use_vp9)
389 : use_vp9_(use_vp9), 367 : use_vp9_(use_vp9),
390 target_bitrate_kbps_(kDefaultTargetBitrateKbps),
391 encode_unchanged_frame_(false),
392 clock_(&default_tick_clock_) { 368 clock_(&default_tick_clock_) {
393 // Indicates config is still uninitialized. 369 // Indicates config is still uninitialized.
394 config_.g_timebase.den = 0; 370 config_.g_timebase.den = 0;
395 } 371 }
396 372
397 void WebrtcVideoEncoderVpx::Configure(const webrtc::DesktopSize& size) { 373 void WebrtcVideoEncoderVpx::Configure(const webrtc::DesktopSize& size) {
398 DCHECK(use_vp9_ || !lossless_color_); 374 DCHECK(use_vp9_ || !lossless_color_);
399 DCHECK(use_vp9_ || !lossless_encode_); 375 DCHECK(use_vp9_ || !lossless_encode_);
400 376
401 // Tear down |image_| if it no longer matches the size and color settings. 377 // Tear down |image_| if it no longer matches the size and color settings.
402 // PrepareImage() will then create a new buffer of the required dimensions if 378 // PrepareImage() will then create a new buffer of the required dimensions if
403 // |image_| is not allocated. 379 // |image_| is not allocated.
404 FreeImageIfMismatched(lossless_color_, size, &image_, &image_buffer_); 380 FreeImageIfMismatched(lossless_color_, size, &image_, &image_buffer_);
405 381
406 // Initialize active map. 382 // Initialize active map.
407 active_map_size_ = webrtc::DesktopSize( 383 active_map_size_ = webrtc::DesktopSize(
408 (size.width() + kMacroBlockSize - 1) / kMacroBlockSize, 384 (size.width() + kMacroBlockSize - 1) / kMacroBlockSize,
409 (size.height() + kMacroBlockSize - 1) / kMacroBlockSize); 385 (size.height() + kMacroBlockSize - 1) / kMacroBlockSize);
410 active_map_.reset( 386 active_map_.reset(
411 new uint8_t[active_map_size_.width() * active_map_size_.height()]); 387 new uint8_t[active_map_size_.width() * active_map_size_.height()]);
388 ClearActiveMap();
412 389
413 // TODO(wez): Remove this hack once VPX can handle frame size reconfiguration. 390 // TODO(wez): Remove this hack once VPX can handle frame size reconfiguration.
414 // See https://code.google.com/p/webm/issues/detail?id=912. 391 // See https://code.google.com/p/webm/issues/detail?id=912.
415 if (codec_) { 392 if (codec_) {
416 // If the frame size has changed then force re-creation of the codec. 393 // If the frame size has changed then force re-creation of the codec.
417 if (codec_->config.enc->g_w != static_cast<unsigned int>(size.width()) || 394 if (codec_->config.enc->g_w != static_cast<unsigned int>(size.width()) ||
418 codec_->config.enc->g_h != static_cast<unsigned int>(size.height())) { 395 codec_->config.enc->g_h != static_cast<unsigned int>(size.height())) {
419 codec_.reset(); 396 codec_.reset();
420 } 397 }
421 } 398 }
422 399
423 // Fetch a default configuration for the desired codec. 400 // Fetch a default configuration for the desired codec.
424 const vpx_codec_iface_t* interface = 401 const vpx_codec_iface_t* interface =
425 use_vp9_ ? vpx_codec_vp9_cx() : vpx_codec_vp8_cx(); 402 use_vp9_ ? vpx_codec_vp9_cx() : vpx_codec_vp8_cx();
426 vpx_codec_err_t ret = vpx_codec_enc_config_default(interface, &config_, 0); 403 vpx_codec_err_t ret = vpx_codec_enc_config_default(interface, &config_, 0);
427 DCHECK_EQ(VPX_CODEC_OK, ret) << "Failed to fetch default configuration"; 404 DCHECK_EQ(VPX_CODEC_OK, ret) << "Failed to fetch default configuration";
428 405
429 // Customize the default configuration to our needs. 406 // Customize the default configuration to our needs.
430 if (use_vp9_) { 407 if (use_vp9_) {
431 SetVp9CodecParameters(&config_, size, lossless_color_, lossless_encode_); 408 SetVp9CodecParameters(&config_, size, lossless_color_, lossless_encode_);
432 } else { 409 } else {
433 SetVp8CodecParameters(&config_, size); 410 SetVp8CodecParameters(&config_, size);
434 } 411 }
435 config_.rc_target_bitrate = target_bitrate_kbps_; 412
413 config_.rc_target_bitrate = kDefaultTargetBitrateKbps;
436 414
437 // Initialize or re-configure the codec with the custom configuration. 415 // Initialize or re-configure the codec with the custom configuration.
438 if (!codec_) { 416 if (!codec_) {
439 codec_.reset(new vpx_codec_ctx_t); 417 codec_.reset(new vpx_codec_ctx_t);
440 ret = vpx_codec_enc_init(codec_.get(), interface, &config_, 0); 418 ret = vpx_codec_enc_init(codec_.get(), interface, &config_, 0);
441 CHECK_EQ(VPX_CODEC_OK, ret) << "Failed to initialize codec"; 419 CHECK_EQ(VPX_CODEC_OK, ret) << "Failed to initialize codec";
442 } else { 420 } else {
443 ret = vpx_codec_enc_config_set(codec_.get(), &config_); 421 ret = vpx_codec_enc_config_set(codec_.get(), &config_);
444 CHECK_EQ(VPX_CODEC_OK, ret) << "Failed to reconfigure codec"; 422 CHECK_EQ(VPX_CODEC_OK, ret) << "Failed to reconfigure codec";
445 } 423 }
446 424
447 // Apply further customizations to the codec now it's initialized. 425 // Apply further customizations to the codec now it's initialized.
448 if (use_vp9_) { 426 if (use_vp9_) {
449 SetVp9CodecOptions(codec_.get(), lossless_encode_); 427 SetVp9CodecOptions(codec_.get(), lossless_encode_);
450 } else { 428 } else {
451 SetVp8CodecOptions(codec_.get()); 429 SetVp8CodecOptions(codec_.get());
452 } 430 }
453 } 431 }
454 432
455 void WebrtcVideoEncoderVpx::UpdateTargetBitrate(int new_bitrate_kbps) { 433 void WebrtcVideoEncoderVpx::UpdateConfig(const FrameParams& params) {
456 target_bitrate_kbps_ = new_bitrate_kbps;
457
458 // Configuration not initialized. 434 // Configuration not initialized.
459 if (config_.g_timebase.den == 0) 435 if (config_.g_timebase.den == 0)
460 return; 436 return;
461 437
462 if (config_.rc_target_bitrate == static_cast<unsigned int>(new_bitrate_kbps)) 438 bool changed = false;
439
440 if (params.bitrate_kbps >= 0 &&
441 config_.rc_target_bitrate !=
442 static_cast<unsigned int>(params.bitrate_kbps)) {
443 config_.rc_target_bitrate = params.bitrate_kbps;
444 changed = true;
445 }
446
447 if (params.vpx_min_quantizer >= 0 &&
448 config_.rc_min_quantizer !=
449 static_cast<unsigned int>(params.vpx_min_quantizer)) {
450 config_.rc_min_quantizer = params.vpx_min_quantizer;
451 changed = true;
452 }
453
454 if (params.vpx_max_quantizer >= 0 &&
455 config_.rc_max_quantizer !=
456 static_cast<unsigned int>(params.vpx_max_quantizer)) {
457 config_.rc_max_quantizer = params.vpx_max_quantizer;
458 changed = true;
459 }
460
461 if (!changed)
463 return; 462 return;
464 config_.rc_target_bitrate = new_bitrate_kbps;
465 463
466 // Update encoder context. 464 // Update encoder context.
467 if (vpx_codec_enc_config_set(codec_.get(), &config_)) 465 if (vpx_codec_enc_config_set(codec_.get(), &config_))
468 NOTREACHED() << "Unable to set encoder config"; 466 NOTREACHED() << "Unable to set encoder config";
469 467
470 VLOG(1) << "New rc_target_bitrate: " << new_bitrate_kbps << " kbps";
471 } 468 }
472 469
473 void WebrtcVideoEncoderVpx::PrepareImage( 470 void WebrtcVideoEncoderVpx::PrepareImage(
474 const webrtc::DesktopFrame& frame, 471 const webrtc::DesktopFrame& frame,
475 webrtc::DesktopRegion* updated_region) { 472 webrtc::DesktopRegion* updated_region) {
476 if (frame.updated_region().is_empty()) { 473 if (frame.updated_region().is_empty()) {
477 updated_region->Clear(); 474 updated_region->Clear();
478 return; 475 return;
479 } 476 }
480 477
481 updated_region->Clear(); 478 updated_region->Clear();
482 if (image_) { 479 if (image_) {
483 // Pad each rectangle to avoid the block-artefact filters in libvpx from 480 // Pad each rectangle to avoid the block-artifact filters in libvpx from
484 // introducing artefacts; VP9 includes up to 8px either side, and VP8 up to 481 // introducing artifacts; VP9 includes up to 8px either side, and VP8 up to
485 // 3px, so unchanged pixels up to that far out may still be affected by the 482 // 3px, so unchanged pixels up to that far out may still be affected by the
486 // changes in the updated region, and so must be listed in the active map. 483 // changes in the updated region, and so must be listed in the active map.
487 // After padding we align each rectangle to 16x16 active-map macroblocks. 484 // After padding we align each rectangle to 16x16 active-map macroblocks.
488 // This implicitly ensures all rects have even top-left coords, which is 485 // This implicitly ensures all rects have even top-left coords, which is
489 // is required by ConvertRGBToYUVWithRect(). 486 // is required by ConvertRGBToYUVWithRect().
490 // TODO(wez): Do we still need 16x16 align, or is even alignment sufficient? 487 // TODO(wez): Do we still need 16x16 align, or is even alignment sufficient?
491 int padding = use_vp9_ ? 8 : 3; 488 int padding = use_vp9_ ? 8 : 3;
492 for (webrtc::DesktopRegion::Iterator r(frame.updated_region()); 489 for (webrtc::DesktopRegion::Iterator r(frame.updated_region());
493 !r.IsAtEnd(); r.Advance()) { 490 !r.IsAtEnd(); r.Advance()) {
494 const webrtc::DesktopRect& rect = r.rect(); 491 const webrtc::DesktopRect& rect = r.rect();
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after
545 v_data + uv_offset, uv_stride, rect.width(), 542 v_data + uv_offset, uv_stride, rect.width(),
546 rect.height()); 543 rect.height());
547 } 544 }
548 break; 545 break;
549 default: 546 default:
550 NOTREACHED(); 547 NOTREACHED();
551 break; 548 break;
552 } 549 }
553 } 550 }
554 551
552 void WebrtcVideoEncoderVpx::ClearActiveMap() {
553 // Clear active map first.
Irfan 2016/09/14 20:01:29 DCHECK(active_map_) ?
Sergey Ulanov 2016/09/16 00:02:47 Done.
554 memset(active_map_.get(), 0,
555 active_map_size_.width() * active_map_size_.height());
556 }
557
555 void WebrtcVideoEncoderVpx::SetActiveMapFromRegion( 558 void WebrtcVideoEncoderVpx::SetActiveMapFromRegion(
556 const webrtc::DesktopRegion& updated_region) { 559 const webrtc::DesktopRegion& updated_region) {
557 // Clear active map first.
558 memset(active_map_.get(), 0,
559 active_map_size_.width() * active_map_size_.height());
560
561 // Mark updated areas active. 560 // Mark updated areas active.
562 for (webrtc::DesktopRegion::Iterator r(updated_region); !r.IsAtEnd(); 561 for (webrtc::DesktopRegion::Iterator r(updated_region); !r.IsAtEnd();
563 r.Advance()) { 562 r.Advance()) {
564 const webrtc::DesktopRect& rect = r.rect(); 563 const webrtc::DesktopRect& rect = r.rect();
565 int left = rect.left() / kMacroBlockSize; 564 int left = rect.left() / kMacroBlockSize;
566 int right = (rect.right() - 1) / kMacroBlockSize; 565 int right = (rect.right() - 1) / kMacroBlockSize;
567 int top = rect.top() / kMacroBlockSize; 566 int top = rect.top() / kMacroBlockSize;
568 int bottom = (rect.bottom() - 1) / kMacroBlockSize; 567 int bottom = (rect.bottom() - 1) / kMacroBlockSize;
569 DCHECK_LT(right, active_map_size_.width()); 568 DCHECK_LT(right, active_map_size_.width());
570 DCHECK_LT(bottom, active_map_size_.height()); 569 DCHECK_LT(bottom, active_map_size_.height());
(...skipping 23 matching lines...) Expand all
594 kMacroBlockSize * (y + 1))); 593 kMacroBlockSize * (y + 1)));
595 } 594 }
596 x0 = x1 + 1; 595 x0 = x1 + 1;
597 } 596 }
598 } 597 }
599 updated_region->IntersectWith( 598 updated_region->IntersectWith(
600 webrtc::DesktopRect::MakeWH(image_->w, image_->h)); 599 webrtc::DesktopRect::MakeWH(image_->w, image_->h));
601 } 600 }
602 601
603 } // namespace remoting 602 } // namespace remoting
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698