| OLD | NEW |
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 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 "media/cast/sender/vp8_encoder.h" | 5 #include "media/cast/sender/vp8_encoder.h" |
| 6 | 6 |
| 7 #include "base/debug/crash_logging.h" | 7 #include "base/debug/crash_logging.h" |
| 8 #include "base/debug/dump_without_crashing.h" | 8 #include "base/debug/dump_without_crashing.h" |
| 9 #include "base/format_macros.h" | 9 #include "base/format_macros.h" |
| 10 #include "base/logging.h" | 10 #include "base/logging.h" |
| (...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 56 | 56 |
| 57 bool HasSufficientFeedback( | 57 bool HasSufficientFeedback( |
| 58 const FeedbackSignalAccumulator<base::TimeDelta>& accumulator) { | 58 const FeedbackSignalAccumulator<base::TimeDelta>& accumulator) { |
| 59 const base::TimeDelta amount_of_history = | 59 const base::TimeDelta amount_of_history = |
| 60 accumulator.update_time() - accumulator.reset_time(); | 60 accumulator.update_time() - accumulator.reset_time(); |
| 61 return amount_of_history.InMicroseconds() >= 250000; // 0.25 second. | 61 return amount_of_history.InMicroseconds() >= 250000; // 0.25 second. |
| 62 } | 62 } |
| 63 | 63 |
| 64 } // namespace | 64 } // namespace |
| 65 | 65 |
| 66 Vp8Encoder::Vp8Encoder(const VideoSenderConfig& video_config) | 66 Vp8Encoder::Vp8Encoder(const FrameSenderConfig& video_config) |
| 67 : cast_config_(video_config), | 67 : cast_config_(video_config), |
| 68 target_encoder_utilization_( | 68 target_encoder_utilization_( |
| 69 video_config.number_of_encode_threads > 2 | 69 video_config.video_codec_params.number_of_encode_threads > 2 |
| 70 ? kHiTargetEncoderUtilization | 70 ? kHiTargetEncoderUtilization |
| 71 : (video_config.number_of_encode_threads > 1 | 71 : (video_config.video_codec_params.number_of_encode_threads > 1 |
| 72 ? kMidTargetEncoderUtilization | 72 ? kMidTargetEncoderUtilization |
| 73 : kLoTargetEncoderUtilization)), | 73 : kLoTargetEncoderUtilization)), |
| 74 key_frame_requested_(true), | 74 key_frame_requested_(true), |
| 75 bitrate_kbit_(cast_config_.start_bitrate / 1000), | 75 bitrate_kbit_(cast_config_.start_bitrate / 1000), |
| 76 next_frame_id_(FrameId::first()), | 76 next_frame_id_(FrameId::first()), |
| 77 has_seen_zero_length_encoded_frame_(false), | 77 has_seen_zero_length_encoded_frame_(false), |
| 78 encoding_speed_acc_( | 78 encoding_speed_acc_( |
| 79 base::TimeDelta::FromMicroseconds(kEncodingSpeedAccHalfLife)), | 79 base::TimeDelta::FromMicroseconds(kEncodingSpeedAccHalfLife)), |
| 80 encoding_speed_(kHighestEncodingSpeed) { | 80 encoding_speed_(kHighestEncodingSpeed) { |
| 81 config_.g_timebase.den = 0; // Not initialized. | 81 config_.g_timebase.den = 0; // Not initialized. |
| 82 DCHECK_LE(cast_config_.min_qp, cast_config_.max_cpu_saver_qp); | 82 DCHECK_LE(cast_config_.video_codec_params.min_qp, |
| 83 DCHECK_LE(cast_config_.max_cpu_saver_qp, cast_config_.max_qp); | 83 cast_config_.video_codec_params.max_cpu_saver_qp); |
| 84 DCHECK_LE(cast_config_.video_codec_params.max_cpu_saver_qp, |
| 85 cast_config_.video_codec_params.max_qp); |
| 84 | 86 |
| 85 thread_checker_.DetachFromThread(); | 87 thread_checker_.DetachFromThread(); |
| 86 } | 88 } |
| 87 | 89 |
| 88 Vp8Encoder::~Vp8Encoder() { | 90 Vp8Encoder::~Vp8Encoder() { |
| 89 DCHECK(thread_checker_.CalledOnValidThread()); | 91 DCHECK(thread_checker_.CalledOnValidThread()); |
| 90 if (is_initialized()) | 92 if (is_initialized()) |
| 91 vpx_codec_destroy(&encoder_); | 93 vpx_codec_destroy(&encoder_); |
| 92 } | 94 } |
| 93 | 95 |
| 94 void Vp8Encoder::Initialize() { | 96 void Vp8Encoder::Initialize() { |
| 95 DCHECK(thread_checker_.CalledOnValidThread()); | 97 DCHECK(thread_checker_.CalledOnValidThread()); |
| 96 DCHECK(!is_initialized()); | 98 DCHECK(!is_initialized()); |
| 97 // The encoder will be created/configured when the first frame encode is | 99 // The encoder will be created/configured when the first frame encode is |
| 98 // requested. | 100 // requested. |
| 99 } | 101 } |
| 100 | 102 |
| 101 void Vp8Encoder::ConfigureForNewFrameSize(const gfx::Size& frame_size) { | 103 void Vp8Encoder::ConfigureForNewFrameSize(const gfx::Size& frame_size) { |
| 102 if (is_initialized()) { | 104 if (is_initialized()) { |
| 103 // Workaround for VP8 bug: If the new size is strictly less-than-or-equal to | 105 // Workaround for VP8 bug: If the new size is strictly less-than-or-equal to |
| 104 // the old size, in terms of area, the existing encoder instance can | 106 // the old size, in terms of area, the existing encoder instance can |
| 105 // continue. Otherwise, completely tear-down and re-create a new encoder to | 107 // continue. Otherwise, completely tear-down and re-create a new encoder to |
| 106 // avoid a shutdown crash. | 108 // avoid a shutdown crash. |
| 107 if (frame_size.GetArea() <= gfx::Size(config_.g_w, config_.g_h).GetArea()) { | 109 if (frame_size.GetArea() <= gfx::Size(config_.g_w, config_.g_h).GetArea()) { |
| 108 DVLOG(1) << "Continuing to use existing encoder at smaller frame size: " | 110 DVLOG(1) << "Continuing to use existing encoder at smaller frame size: " |
| 109 << gfx::Size(config_.g_w, config_.g_h).ToString() << " --> " | 111 << gfx::Size(config_.g_w, config_.g_h).ToString() << " --> " |
| 110 << frame_size.ToString(); | 112 << frame_size.ToString(); |
| 111 config_.g_w = frame_size.width(); | 113 config_.g_w = frame_size.width(); |
| 112 config_.g_h = frame_size.height(); | 114 config_.g_h = frame_size.height(); |
| 113 config_.rc_min_quantizer = cast_config_.min_qp; | 115 config_.rc_min_quantizer = cast_config_.video_codec_params.min_qp; |
| 114 if (vpx_codec_enc_config_set(&encoder_, &config_) == VPX_CODEC_OK) | 116 if (vpx_codec_enc_config_set(&encoder_, &config_) == VPX_CODEC_OK) |
| 115 return; | 117 return; |
| 116 DVLOG(1) << "libvpx rejected the attempt to use a smaller frame size in " | 118 DVLOG(1) << "libvpx rejected the attempt to use a smaller frame size in " |
| 117 "the current instance."; | 119 "the current instance."; |
| 118 } | 120 } |
| 119 | 121 |
| 120 DVLOG(1) << "Destroying/Re-Creating encoder for larger frame size: " | 122 DVLOG(1) << "Destroying/Re-Creating encoder for larger frame size: " |
| 121 << gfx::Size(config_.g_w, config_.g_h).ToString() << " --> " | 123 << gfx::Size(config_.g_w, config_.g_h).ToString() << " --> " |
| 122 << frame_size.ToString(); | 124 << frame_size.ToString(); |
| 123 vpx_codec_destroy(&encoder_); | 125 vpx_codec_destroy(&encoder_); |
| 124 } else { | 126 } else { |
| 125 DVLOG(1) << "Creating encoder for the first frame; size: " | 127 DVLOG(1) << "Creating encoder for the first frame; size: " |
| 126 << frame_size.ToString(); | 128 << frame_size.ToString(); |
| 127 } | 129 } |
| 128 | 130 |
| 129 // Populate encoder configuration with default values. | 131 // Populate encoder configuration with default values. |
| 130 CHECK_EQ(vpx_codec_enc_config_default(vpx_codec_vp8_cx(), &config_, 0), | 132 CHECK_EQ(vpx_codec_enc_config_default(vpx_codec_vp8_cx(), &config_, 0), |
| 131 VPX_CODEC_OK); | 133 VPX_CODEC_OK); |
| 132 | 134 |
| 133 config_.g_threads = cast_config_.number_of_encode_threads; | 135 config_.g_threads = cast_config_.video_codec_params.number_of_encode_threads; |
| 134 config_.g_w = frame_size.width(); | 136 config_.g_w = frame_size.width(); |
| 135 config_.g_h = frame_size.height(); | 137 config_.g_h = frame_size.height(); |
| 136 // Set the timebase to match that of base::TimeDelta. | 138 // Set the timebase to match that of base::TimeDelta. |
| 137 config_.g_timebase.num = 1; | 139 config_.g_timebase.num = 1; |
| 138 config_.g_timebase.den = base::Time::kMicrosecondsPerSecond; | 140 config_.g_timebase.den = base::Time::kMicrosecondsPerSecond; |
| 139 | 141 |
| 140 // |g_pass| and |g_lag_in_frames| must be "one pass" and zero, respectively, | 142 // |g_pass| and |g_lag_in_frames| must be "one pass" and zero, respectively, |
| 141 // in order for VP8 to support changing frame sizes during encoding: | 143 // in order for VP8 to support changing frame sizes during encoding: |
| 142 config_.g_pass = VPX_RC_ONE_PASS; | 144 config_.g_pass = VPX_RC_ONE_PASS; |
| 143 config_.g_lag_in_frames = 0; // Immediate data output for each frame. | 145 config_.g_lag_in_frames = 0; // Immediate data output for each frame. |
| 144 | 146 |
| 145 // Rate control settings. | 147 // Rate control settings. |
| 146 config_.rc_dropframe_thresh = 0; // The encoder may not drop any frames. | 148 config_.rc_dropframe_thresh = 0; // The encoder may not drop any frames. |
| 147 config_.rc_resize_allowed = 0; // TODO(miu): Why not? Investigate this. | 149 config_.rc_resize_allowed = 0; // TODO(miu): Why not? Investigate this. |
| 148 config_.rc_end_usage = VPX_CBR; | 150 config_.rc_end_usage = VPX_CBR; |
| 149 config_.rc_target_bitrate = bitrate_kbit_; | 151 config_.rc_target_bitrate = bitrate_kbit_; |
| 150 config_.rc_min_quantizer = cast_config_.min_qp; | 152 config_.rc_min_quantizer = cast_config_.video_codec_params.min_qp; |
| 151 config_.rc_max_quantizer = cast_config_.max_qp; | 153 config_.rc_max_quantizer = cast_config_.video_codec_params.max_qp; |
| 152 // TODO(miu): Revisit these now that the encoder is being successfully | 154 // TODO(miu): Revisit these now that the encoder is being successfully |
| 153 // micro-managed. | 155 // micro-managed. |
| 154 config_.rc_undershoot_pct = 100; | 156 config_.rc_undershoot_pct = 100; |
| 155 config_.rc_overshoot_pct = 15; | 157 config_.rc_overshoot_pct = 15; |
| 156 // TODO(miu): Document why these rc_buf_*_sz values were chosen and/or | 158 // TODO(miu): Document why these rc_buf_*_sz values were chosen and/or |
| 157 // research for better values. Should they be computed from the target | 159 // research for better values. Should they be computed from the target |
| 158 // playout delay? | 160 // playout delay? |
| 159 config_.rc_buf_initial_sz = 500; | 161 config_.rc_buf_initial_sz = 500; |
| 160 config_.rc_buf_optimal_sz = 600; | 162 config_.rc_buf_optimal_sz = 600; |
| 161 config_.rc_buf_sz = 1000; | 163 config_.rc_buf_sz = 1000; |
| (...skipping 182 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 344 key_frame_requested_ = false; | 346 key_frame_requested_ = false; |
| 345 } | 347 } |
| 346 if (encoded_frame->dependency == EncodedFrame::KEY) { | 348 if (encoded_frame->dependency == EncodedFrame::KEY) { |
| 347 encoding_speed_acc_.Reset(kHighestEncodingSpeed, video_frame->timestamp()); | 349 encoding_speed_acc_.Reset(kHighestEncodingSpeed, video_frame->timestamp()); |
| 348 } else { | 350 } else { |
| 349 // Equivalent encoding speed considering both cpu_used setting and | 351 // Equivalent encoding speed considering both cpu_used setting and |
| 350 // quantizer. | 352 // quantizer. |
| 351 double actual_encoding_speed = | 353 double actual_encoding_speed = |
| 352 encoding_speed_ + | 354 encoding_speed_ + |
| 353 kEquivalentEncodingSpeedStepPerQpStep * | 355 kEquivalentEncodingSpeedStepPerQpStep * |
| 354 std::max(0, quantizer - cast_config_.min_qp); | 356 std::max(0, quantizer - cast_config_.video_codec_params.min_qp); |
| 355 double adjusted_encoding_speed = actual_encoding_speed * | 357 double adjusted_encoding_speed = actual_encoding_speed * |
| 356 encoded_frame->encoder_utilization / | 358 encoded_frame->encoder_utilization / |
| 357 target_encoder_utilization_; | 359 target_encoder_utilization_; |
| 358 encoding_speed_acc_.Update(adjusted_encoding_speed, | 360 encoding_speed_acc_.Update(adjusted_encoding_speed, |
| 359 video_frame->timestamp()); | 361 video_frame->timestamp()); |
| 360 } | 362 } |
| 361 | 363 |
| 362 if (HasSufficientFeedback(encoding_speed_acc_)) { | 364 if (HasSufficientFeedback(encoding_speed_acc_)) { |
| 363 // Predict |encoding_speed_| and |min_quantizer| for next frame. | 365 // Predict |encoding_speed_| and |min_quantizer| for next frame. |
| 364 // When CPU is constrained, increase encoding speed and increase | 366 // When CPU is constrained, increase encoding speed and increase |
| 365 // |min_quantizer| if needed. | 367 // |min_quantizer| if needed. |
| 366 double next_encoding_speed = encoding_speed_acc_.current(); | 368 double next_encoding_speed = encoding_speed_acc_.current(); |
| 367 int next_min_qp; | 369 int next_min_qp; |
| 368 if (next_encoding_speed > kHighestEncodingSpeed) { | 370 if (next_encoding_speed > kHighestEncodingSpeed) { |
| 369 double remainder = next_encoding_speed - kHighestEncodingSpeed; | 371 double remainder = next_encoding_speed - kHighestEncodingSpeed; |
| 370 next_encoding_speed = kHighestEncodingSpeed; | 372 next_encoding_speed = kHighestEncodingSpeed; |
| 371 next_min_qp = | 373 next_min_qp = |
| 372 static_cast<int>(remainder / kEquivalentEncodingSpeedStepPerQpStep + | 374 static_cast<int>(remainder / kEquivalentEncodingSpeedStepPerQpStep + |
| 373 cast_config_.min_qp + 0.5); | 375 cast_config_.video_codec_params.min_qp + 0.5); |
| 374 next_min_qp = std::min(next_min_qp, cast_config_.max_cpu_saver_qp); | 376 next_min_qp = std::min(next_min_qp, |
| 377 cast_config_.video_codec_params.max_cpu_saver_qp); |
| 375 } else { | 378 } else { |
| 376 next_encoding_speed = | 379 next_encoding_speed = |
| 377 std::max<double>(kLowestEncodingSpeed, next_encoding_speed) + 0.5; | 380 std::max<double>(kLowestEncodingSpeed, next_encoding_speed) + 0.5; |
| 378 next_min_qp = cast_config_.min_qp; | 381 next_min_qp = cast_config_.video_codec_params.min_qp; |
| 379 } | 382 } |
| 380 if (encoding_speed_ != static_cast<int>(next_encoding_speed)) { | 383 if (encoding_speed_ != static_cast<int>(next_encoding_speed)) { |
| 381 encoding_speed_ = static_cast<int>(next_encoding_speed); | 384 encoding_speed_ = static_cast<int>(next_encoding_speed); |
| 382 CHECK_EQ(vpx_codec_control(&encoder_, VP8E_SET_CPUUSED, -encoding_speed_), | 385 CHECK_EQ(vpx_codec_control(&encoder_, VP8E_SET_CPUUSED, -encoding_speed_), |
| 383 VPX_CODEC_OK); | 386 VPX_CODEC_OK); |
| 384 } | 387 } |
| 385 if (config_.rc_min_quantizer != static_cast<unsigned int>(next_min_qp)) { | 388 if (config_.rc_min_quantizer != static_cast<unsigned int>(next_min_qp)) { |
| 386 config_.rc_min_quantizer = static_cast<unsigned int>(next_min_qp); | 389 config_.rc_min_quantizer = static_cast<unsigned int>(next_min_qp); |
| 387 CHECK_EQ(vpx_codec_enc_config_set(&encoder_, &config_), VPX_CODEC_OK); | 390 CHECK_EQ(vpx_codec_enc_config_set(&encoder_, &config_), VPX_CODEC_OK); |
| 388 } | 391 } |
| (...skipping 20 matching lines...) Expand all Loading... |
| 409 VLOG(1) << "VP8 new rc_target_bitrate: " << new_bitrate_kbit << " kbps"; | 412 VLOG(1) << "VP8 new rc_target_bitrate: " << new_bitrate_kbit << " kbps"; |
| 410 } | 413 } |
| 411 | 414 |
| 412 void Vp8Encoder::GenerateKeyFrame() { | 415 void Vp8Encoder::GenerateKeyFrame() { |
| 413 DCHECK(thread_checker_.CalledOnValidThread()); | 416 DCHECK(thread_checker_.CalledOnValidThread()); |
| 414 key_frame_requested_ = true; | 417 key_frame_requested_ = true; |
| 415 } | 418 } |
| 416 | 419 |
| 417 } // namespace cast | 420 } // namespace cast |
| 418 } // namespace media | 421 } // namespace media |
| OLD | NEW |