| 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/external_video_encoder.h" | 5 #include "media/cast/sender/external_video_encoder.h" |
| 6 | 6 |
| 7 #include <cmath> | 7 #include <cmath> |
| 8 #include <utility> | 8 #include <utility> |
| 9 | 9 |
| 10 #include "base/bind.h" | 10 #include "base/bind.h" |
| (...skipping 26 matching lines...) Expand all Loading... |
| 37 | 37 |
| 38 enum { MAX_H264_QUANTIZER = 51 }; | 38 enum { MAX_H264_QUANTIZER = 51 }; |
| 39 | 39 |
| 40 // Number of buffers for encoded bit stream. | 40 // Number of buffers for encoded bit stream. |
| 41 constexpr size_t kOutputBufferCount = 3; | 41 constexpr size_t kOutputBufferCount = 3; |
| 42 | 42 |
| 43 // Maximum number of extra input buffers for encoder. The input buffers are only | 43 // Maximum number of extra input buffers for encoder. The input buffers are only |
| 44 // used when copy is needed to match the required coded size. | 44 // used when copy is needed to match the required coded size. |
| 45 constexpr size_t kExtraInputBufferCount = 2; | 45 constexpr size_t kExtraInputBufferCount = 2; |
| 46 | 46 |
| 47 // Parses the command-line flag and returns 0 (default) to use the old/flawed | 47 // This value is used to calculate the encoder utilization. The encoder is |
| 48 // "deadline utilization" heuristic to measure encoder utilization. Otherwise, | 48 // assumed to be in full usage when the number of frames in progress reaches it. |
| 49 // returns the "redline" value for the "backlog" heuristic (i.e., num_frames / | 49 constexpr int kBacklogRedlineThreshold = 4; |
| 50 // redline = utilization). | |
| 51 // | |
| 52 // Example command line switches and results: | |
| 53 // | |
| 54 // --cast-encoder-util-heuristic=foobar ==> 0 (unrecognized, use deadline) | |
| 55 // --cast-encoder-util-heuristic=backlog ==> 6 (use backlog, default) | |
| 56 // --cast-encoder-util-heuristic=backlog7 ==> 7 (use backlog, redline=7) | |
| 57 // | |
| 58 // TODO(miu): This is temporary, for lab performance testing, until a | |
| 59 // good "works for all" solution is confirmed. | |
| 60 // https://code.google.com/p/chrome-os-partner/issues/detail?id=54806 | |
| 61 int GetConfiguredBacklogRedline() { | |
| 62 constexpr char kBacklogSwitchValue[] = "backlog"; | |
| 63 constexpr int kBacklogDefaultRedline = 6; | |
| 64 | |
| 65 const std::string& switch_value = | |
| 66 base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII( | |
| 67 switches::kCastEncoderUtilHeuristic); | |
| 68 if (!base::StartsWith(switch_value, kBacklogSwitchValue, | |
| 69 base::CompareCase::SENSITIVE)) { | |
| 70 return 0; | |
| 71 } | |
| 72 | |
| 73 int redline = kBacklogDefaultRedline; | |
| 74 if (!base::StringToInt(switch_value.substr(sizeof(kBacklogSwitchValue) - 1), | |
| 75 &redline)) { | |
| 76 redline = kBacklogDefaultRedline; | |
| 77 } | |
| 78 VLOG(1) << "Using 'backlog' heuristic with a redline of " << redline | |
| 79 << " to compute encoder utilization."; | |
| 80 return redline; | |
| 81 } | |
| 82 | 50 |
| 83 } // namespace | 51 } // namespace |
| 84 | 52 |
| 85 namespace media { | 53 namespace media { |
| 86 namespace cast { | 54 namespace cast { |
| 87 | 55 |
| 88 // Container for the associated data of a video frame being processed. | 56 // Container for the associated data of a video frame being processed. |
| 89 struct InProgressFrameEncode { | 57 struct InProgressFrameEncode { |
| 90 // The source content to encode. | 58 // The source content to encode. |
| 91 const scoped_refptr<VideoFrame> video_frame; | 59 const scoped_refptr<VideoFrame> video_frame; |
| (...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 129 std::unique_ptr<media::VideoEncodeAccelerator> vea, | 97 std::unique_ptr<media::VideoEncodeAccelerator> vea, |
| 130 double max_frame_rate, | 98 double max_frame_rate, |
| 131 const StatusChangeCallback& status_change_cb, | 99 const StatusChangeCallback& status_change_cb, |
| 132 const CreateVideoEncodeMemoryCallback& create_video_encode_memory_cb) | 100 const CreateVideoEncodeMemoryCallback& create_video_encode_memory_cb) |
| 133 : cast_environment_(cast_environment), | 101 : cast_environment_(cast_environment), |
| 134 task_runner_(encoder_task_runner), | 102 task_runner_(encoder_task_runner), |
| 135 max_frame_rate_(max_frame_rate), | 103 max_frame_rate_(max_frame_rate), |
| 136 status_change_cb_(status_change_cb), | 104 status_change_cb_(status_change_cb), |
| 137 create_video_encode_memory_cb_(create_video_encode_memory_cb), | 105 create_video_encode_memory_cb_(create_video_encode_memory_cb), |
| 138 video_encode_accelerator_(std::move(vea)), | 106 video_encode_accelerator_(std::move(vea)), |
| 139 backlog_redline_threshold_(GetConfiguredBacklogRedline()), | |
| 140 encoder_active_(false), | 107 encoder_active_(false), |
| 141 next_frame_id_(FrameId::first()), | 108 next_frame_id_(FrameId::first()), |
| 142 key_frame_encountered_(false), | 109 key_frame_encountered_(false), |
| 143 codec_profile_(media::VIDEO_CODEC_PROFILE_UNKNOWN), | 110 codec_profile_(media::VIDEO_CODEC_PROFILE_UNKNOWN), |
| 144 key_frame_quantizer_parsable_(false), | 111 key_frame_quantizer_parsable_(false), |
| 145 requested_bit_rate_(-1), | 112 requested_bit_rate_(-1), |
| 146 has_seen_zero_length_encoded_frame_(false), | 113 has_seen_zero_length_encoded_frame_(false), |
| 147 max_allowed_input_buffers_(0), | 114 max_allowed_input_buffers_(0), |
| 148 allocate_input_buffer_in_progress_(false) {} | 115 allocate_input_buffer_in_progress_(false) {} |
| 149 | 116 |
| (...skipping 191 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 341 } | 308 } |
| 342 encoded_frame->data.append( | 309 encoded_frame->data.append( |
| 343 static_cast<const char*>(output_buffer->memory()), payload_size); | 310 static_cast<const char*>(output_buffer->memory()), payload_size); |
| 344 | 311 |
| 345 // If FRAME_DURATION metadata was provided in the source VideoFrame, | 312 // If FRAME_DURATION metadata was provided in the source VideoFrame, |
| 346 // compute the utilization metrics. | 313 // compute the utilization metrics. |
| 347 base::TimeDelta frame_duration; | 314 base::TimeDelta frame_duration; |
| 348 if (request.video_frame->metadata()->GetTimeDelta( | 315 if (request.video_frame->metadata()->GetTimeDelta( |
| 349 media::VideoFrameMetadata::FRAME_DURATION, &frame_duration) && | 316 media::VideoFrameMetadata::FRAME_DURATION, &frame_duration) && |
| 350 frame_duration > base::TimeDelta()) { | 317 frame_duration > base::TimeDelta()) { |
| 351 if (backlog_redline_threshold_ == 0) { | 318 // Compute encoder utilization in terms of the number of frames in |
| 352 // Compute encoder utilization as the real-world time elapsed divided | 319 // backlog, including the current frame encode that is finishing |
| 353 // by the frame duration. | 320 // here. This "backlog" model works as follows: First, assume that all |
| 354 const base::TimeDelta processing_time = | 321 // frames utilize the encoder by the same amount. This is actually a |
| 355 base::TimeTicks::Now() - request.start_time; | 322 // false assumption, but it still works well because any frame that |
| 356 encoded_frame->encoder_utilization = | 323 // takes longer to encode will naturally cause the backlog to |
| 357 processing_time.InSecondsF() / frame_duration.InSecondsF(); | 324 // increase, and this will result in a higher computed utilization for |
| 358 } else { | 325 // the offending frame. If the backlog continues to increase, because |
| 359 // Compute encoder utilization in terms of the number of frames in | 326 // the following frames are also taking too long to encode, the |
| 360 // backlog, including the current frame encode that is finishing | 327 // computed utilization for each successive frame will be higher. At |
| 361 // here. This "backlog" model works as follows: First, assume that all | 328 // some point, upstream control logic will decide that the data volume |
| 362 // frames utilize the encoder by the same amount. This is actually a | 329 // must be reduced. |
| 363 // false assumption, but it still works well because any frame that | 330 encoded_frame->encoder_utilization = |
| 364 // takes longer to encode will naturally cause the backlog to | 331 static_cast<double>(in_progress_frame_encodes_.size()) / |
| 365 // increase, and this will result in a higher computed utilization for | 332 kBacklogRedlineThreshold; |
| 366 // the offending frame. If the backlog continues to increase, because | |
| 367 // the following frames are also taking too long to encode, the | |
| 368 // computed utilization for each successive frame will be higher. At | |
| 369 // some point, upstream control logic will decide that the data volume | |
| 370 // must be reduced. | |
| 371 encoded_frame->encoder_utilization = | |
| 372 static_cast<double>(in_progress_frame_encodes_.size()) / | |
| 373 backlog_redline_threshold_; | |
| 374 } | |
| 375 | 333 |
| 376 const double actual_bit_rate = | 334 const double actual_bit_rate = |
| 377 encoded_frame->data.size() * 8.0 / frame_duration.InSecondsF(); | 335 encoded_frame->data.size() * 8.0 / frame_duration.InSecondsF(); |
| 378 DCHECK_GT(request.target_bit_rate, 0); | 336 DCHECK_GT(request.target_bit_rate, 0); |
| 379 const double bitrate_utilization = | 337 const double bitrate_utilization = |
| 380 actual_bit_rate / request.target_bit_rate; | 338 actual_bit_rate / request.target_bit_rate; |
| 381 double quantizer = QuantizerEstimator::NO_RESULT; | 339 double quantizer = QuantizerEstimator::NO_RESULT; |
| 382 // If the quantizer can be parsed from the key frame, try to parse | 340 // If the quantizer can be parsed from the key frame, try to parse |
| 383 // the following delta frames as well. | 341 // the following delta frames as well. |
| 384 // Otherwise, switch back to entropy estimation for the key frame | 342 // Otherwise, switch back to entropy estimation for the key frame |
| (...skipping 207 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 592 } | 550 } |
| 593 return (num_slices == 0) ? -1 : (total_quantizer / num_slices); | 551 return (num_slices == 0) ? -1 : (total_quantizer / num_slices); |
| 594 } | 552 } |
| 595 | 553 |
| 596 const scoped_refptr<CastEnvironment> cast_environment_; | 554 const scoped_refptr<CastEnvironment> cast_environment_; |
| 597 const scoped_refptr<base::SingleThreadTaskRunner> task_runner_; | 555 const scoped_refptr<base::SingleThreadTaskRunner> task_runner_; |
| 598 const double max_frame_rate_; | 556 const double max_frame_rate_; |
| 599 const StatusChangeCallback status_change_cb_; // Must be run on MAIN thread. | 557 const StatusChangeCallback status_change_cb_; // Must be run on MAIN thread. |
| 600 const CreateVideoEncodeMemoryCallback create_video_encode_memory_cb_; | 558 const CreateVideoEncodeMemoryCallback create_video_encode_memory_cb_; |
| 601 std::unique_ptr<media::VideoEncodeAccelerator> video_encode_accelerator_; | 559 std::unique_ptr<media::VideoEncodeAccelerator> video_encode_accelerator_; |
| 602 const int backlog_redline_threshold_; | |
| 603 bool encoder_active_; | 560 bool encoder_active_; |
| 604 FrameId next_frame_id_; | 561 FrameId next_frame_id_; |
| 605 bool key_frame_encountered_; | 562 bool key_frame_encountered_; |
| 606 std::string stream_header_; | 563 std::string stream_header_; |
| 607 VideoCodecProfile codec_profile_; | 564 VideoCodecProfile codec_profile_; |
| 608 bool key_frame_quantizer_parsable_; | 565 bool key_frame_quantizer_parsable_; |
| 609 H264Parser h264_parser_; | 566 H264Parser h264_parser_; |
| 610 | 567 |
| 611 // Shared memory buffers for output with the VideoAccelerator. | 568 // Shared memory buffers for output with the VideoAccelerator. |
| 612 std::vector<std::unique_ptr<base::SharedMemory>> output_buffers_; | 569 std::vector<std::unique_ptr<base::SharedMemory>> output_buffers_; |
| (...skipping 341 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 954 const double kEntropyAtMaxQuantizer = 7.5; | 911 const double kEntropyAtMaxQuantizer = 7.5; |
| 955 const double slope = | 912 const double slope = |
| 956 (MAX_VP8_QUANTIZER - MIN_VP8_QUANTIZER) / kEntropyAtMaxQuantizer; | 913 (MAX_VP8_QUANTIZER - MIN_VP8_QUANTIZER) / kEntropyAtMaxQuantizer; |
| 957 const double quantizer = std::min<double>( | 914 const double quantizer = std::min<double>( |
| 958 MAX_VP8_QUANTIZER, MIN_VP8_QUANTIZER + slope * shannon_entropy); | 915 MAX_VP8_QUANTIZER, MIN_VP8_QUANTIZER + slope * shannon_entropy); |
| 959 return quantizer; | 916 return quantizer; |
| 960 } | 917 } |
| 961 | 918 |
| 962 } // namespace cast | 919 } // namespace cast |
| 963 } // namespace media | 920 } // namespace media |
| OLD | NEW |