video_capture_oracle.cc 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657
  1. // Copyright (c) 2015 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. #include "media/capture/content/video_capture_oracle.h"
  5. #include <algorithm>
  6. #include <limits>
  7. #include <utility>
  8. #include "base/callback.h"
  9. #include "base/compiler_specific.h"
  10. #include "base/format_macros.h"
  11. #include "base/logging.h"
  12. #include "base/numerics/safe_conversions.h"
  13. #include "base/strings/stringprintf.h"
  14. namespace media {
  15. namespace {
  16. // When a non-compositor event arrives after animation has halted, this
  17. // controls how much time must elapse before deciding to allow a capture.
  18. constexpr auto kAnimationHaltPeriodBeforeCaptureAllowed =
  19. base::Milliseconds(250);
  20. // When estimating frame durations, this is the hard upper-bound on the
  21. // estimate.
  22. constexpr auto kUpperBoundsDurationEstimate = base::Seconds(1);
  23. // The half-life of data points provided to the accumulator used when evaluating
  24. // the recent utilization of the buffer pool. This value is based on a
  25. // simulation, and reacts quickly to change to avoid depleting the buffer pool
  26. // (which would cause hard frame drops).
  27. constexpr auto kBufferUtilizationEvaluationInterval = base::Milliseconds(200);
  28. // The half-life of data points provided to the accumulator used when evaluating
  29. // the recent resource utilization of the consumer. The trade-off made here is
  30. // reaction time versus over-reacting to outlier data points.
  31. constexpr auto kConsumerCapabilityEvaluationInterval = base::Seconds(1);
  32. // The maximum amount of time that may elapse without a feedback update. Any
  33. // longer, and currently-accumulated feedback is not considered recent enough to
  34. // base decisions off of. This prevents changes to the capture size when there
  35. // is an unexpected pause in events.
  36. constexpr auto kMaxTimeSinceLastFeedbackUpdate = base::Seconds(1);
  37. // The amount of additional time, since content animation was last detected, to
  38. // continue being extra-careful about increasing the capture size. This is used
  39. // to prevent breif periods of non-animating content from throwing off the
  40. // heuristics that decide whether to increase the capture size.
  41. constexpr auto kDebouncingPeriodForAnimatedContent = base::Seconds(3);
  42. // When content is animating, this is the length of time the system must be
  43. // contiguously under-utilized before increasing the capture size.
  44. constexpr auto kProvingPeriodForAnimatedContent = base::Seconds(30);
  45. // Given the amount of time between frames, compare to the expected amount of
  46. // time between frames at |frame_rate| and return the fractional difference.
  47. double FractionFromExpectedFrameRate(base::TimeDelta delta, int frame_rate) {
  48. DCHECK_GT(frame_rate, 0);
  49. const base::TimeDelta expected_delta = base::Seconds(1) / frame_rate;
  50. return (delta - expected_delta) / expected_delta;
  51. }
  52. // Returns the next-higher TimeTicks value.
  53. base::TimeTicks JustAfter(base::TimeTicks t) {
  54. return t + base::Microseconds(1);
  55. }
  56. } // anonymous namespace
  57. // static
  58. constexpr base::TimeDelta VideoCaptureOracle::kDefaultMinCapturePeriod;
  59. // static
  60. constexpr base::TimeDelta VideoCaptureOracle::kDefaultMinSizeChangePeriod;
  61. VideoCaptureOracle::VideoCaptureOracle(bool enable_auto_throttling)
  62. : capture_size_throttling_mode_(
  63. enable_auto_throttling ? kThrottlingEnabled : kThrottlingDisabled),
  64. min_size_change_period_(kDefaultMinSizeChangePeriod),
  65. next_frame_number_(0),
  66. last_successfully_delivered_frame_number_(-1),
  67. num_frames_pending_(0),
  68. smoothing_sampler_(kDefaultMinCapturePeriod),
  69. content_sampler_(kDefaultMinCapturePeriod),
  70. min_capture_period_(kDefaultMinCapturePeriod),
  71. buffer_pool_utilization_(kBufferUtilizationEvaluationInterval),
  72. estimated_capable_area_(kConsumerCapabilityEvaluationInterval) {
  73. VLOG(1) << "Capture size auto-throttling is now "
  74. << (enable_auto_throttling ? "enabled." : "disabled.");
  75. }
  76. VideoCaptureOracle::~VideoCaptureOracle() = default;
  77. void VideoCaptureOracle::SetMinCapturePeriod(base::TimeDelta period) {
  78. DCHECK_GT(period, base::TimeDelta());
  79. min_capture_period_ = period;
  80. smoothing_sampler_.SetMinCapturePeriod(period);
  81. content_sampler_.SetMinCapturePeriod(period);
  82. }
  83. void VideoCaptureOracle::SetCaptureSizeConstraints(
  84. const gfx::Size& min_size,
  85. const gfx::Size& max_size,
  86. bool use_fixed_aspect_ratio) {
  87. resolution_chooser_.SetConstraints(min_size, max_size,
  88. use_fixed_aspect_ratio);
  89. }
  90. void VideoCaptureOracle::SetAutoThrottlingEnabled(bool enabled) {
  91. const bool was_enabled =
  92. (capture_size_throttling_mode_ != kThrottlingDisabled);
  93. if (was_enabled == enabled)
  94. return;
  95. capture_size_throttling_mode_ =
  96. enabled ? kThrottlingEnabled : kThrottlingDisabled;
  97. VLOG(1) << "Capture size auto-throttling is now "
  98. << (enabled ? "enabled." : "disabled.");
  99. // When not auto-throttling, have the CaptureResolutionChooser target the max
  100. // resolution within constraints.
  101. if (!enabled)
  102. resolution_chooser_.SetTargetFrameArea(std::numeric_limits<int>::max());
  103. if (next_frame_number_ > 0)
  104. CommitCaptureSizeAndReset(GetFrameTimestamp(next_frame_number_ - 1));
  105. }
  106. void VideoCaptureOracle::SetSourceSize(const gfx::Size& source_size) {
  107. resolution_chooser_.SetSourceSize(source_size);
  108. // If the |resolution_chooser_| computed a new capture size, that will become
  109. // visible via a future call to ObserveEventAndDecideCapture().
  110. source_size_change_time_ = (next_frame_number_ == 0) ?
  111. base::TimeTicks() : GetFrameTimestamp(next_frame_number_ - 1);
  112. }
  113. bool VideoCaptureOracle::ObserveEventAndDecideCapture(
  114. Event event,
  115. const gfx::Rect& damage_rect,
  116. base::TimeTicks event_time) {
  117. DCHECK_GE(event, 0);
  118. DCHECK_LT(event, kNumEvents);
  119. if (event_time < last_event_time_[event]) {
  120. LOG(WARNING) << "Event time is not monotonically non-decreasing. "
  121. << "Deciding not to capture this frame.";
  122. return false;
  123. }
  124. last_event_time_[event] = event_time;
  125. bool should_sample = false;
  126. duration_of_next_frame_ = base::TimeDelta();
  127. switch (event) {
  128. // Refresh demands get the same priority as compositor updates.
  129. case kRefreshDemand:
  130. [[fallthrough]];
  131. case kCompositorUpdate: {
  132. smoothing_sampler_.ConsiderPresentationEvent(event_time);
  133. const bool had_proposal = content_sampler_.HasProposal();
  134. content_sampler_.ConsiderPresentationEvent(damage_rect, event_time);
  135. if (content_sampler_.HasProposal()) {
  136. VLOG_IF(1, !had_proposal) << "Content sampler now detects animation.";
  137. should_sample = content_sampler_.ShouldSample();
  138. if (should_sample) {
  139. event_time = content_sampler_.frame_timestamp();
  140. duration_of_next_frame_ = content_sampler_.sampling_period();
  141. }
  142. last_time_animation_was_detected_ = event_time;
  143. } else {
  144. VLOG_IF(1, had_proposal) << "Content sampler detects animation ended.";
  145. should_sample = smoothing_sampler_.ShouldSample();
  146. }
  147. break;
  148. }
  149. case kRefreshRequest:
  150. // Only allow non-compositor samplings when content has not recently been
  151. // animating, and only if there are no samplings currently in progress.
  152. if (num_frames_pending_ == 0) {
  153. if (!content_sampler_.HasProposal() ||
  154. ((event_time - last_time_animation_was_detected_) >
  155. kAnimationHaltPeriodBeforeCaptureAllowed)) {
  156. smoothing_sampler_.ConsiderPresentationEvent(event_time);
  157. should_sample = smoothing_sampler_.ShouldSample();
  158. }
  159. }
  160. break;
  161. case kNumEvents:
  162. NOTREACHED();
  163. break;
  164. }
  165. if (!should_sample)
  166. return false;
  167. // If the exact duration of the next frame has not been determined, estimate
  168. // it using the difference between the current and last frame.
  169. if (duration_of_next_frame_.is_zero()) {
  170. if (next_frame_number_ > 0) {
  171. duration_of_next_frame_ =
  172. event_time - GetFrameTimestamp(next_frame_number_ - 1);
  173. }
  174. duration_of_next_frame_ = std::max(
  175. std::min(duration_of_next_frame_, kUpperBoundsDurationEstimate),
  176. min_capture_period());
  177. }
  178. // Update |capture_size_| and reset all feedback signal accumulators if
  179. // either: 1) this is the first frame; or 2) |resolution_chooser_| has an
  180. // updated capture size and sufficient time has passed since the last size
  181. // change.
  182. if (next_frame_number_ == 0) {
  183. CommitCaptureSizeAndReset(event_time - duration_of_next_frame_);
  184. } else if (capture_size_ != resolution_chooser_.capture_size()) {
  185. const base::TimeDelta time_since_last_change =
  186. event_time - buffer_pool_utilization_.reset_time();
  187. if (time_since_last_change >= min_size_change_period_ ||
  188. capture_size_throttling_mode_ != kThrottlingActive) {
  189. // Unless autothrottling has become active resolution should be changed
  190. // ASAP.
  191. CommitCaptureSizeAndReset(GetFrameTimestamp(next_frame_number_ - 1));
  192. }
  193. }
  194. SetFrameTimestamp(next_frame_number_, event_time);
  195. return true;
  196. }
  197. void VideoCaptureOracle::RecordCapture(double pool_utilization) {
  198. DCHECK(std::isfinite(pool_utilization) && pool_utilization >= 0.0);
  199. smoothing_sampler_.RecordSample();
  200. const base::TimeTicks timestamp = GetFrameTimestamp(next_frame_number_);
  201. content_sampler_.RecordSample(timestamp);
  202. if (capture_size_throttling_mode_ == kThrottlingActive) {
  203. buffer_pool_utilization_.Update(pool_utilization, timestamp);
  204. AnalyzeAndAdjust(timestamp);
  205. }
  206. num_frames_pending_++;
  207. next_frame_number_++;
  208. }
  209. void VideoCaptureOracle::RecordWillNotCapture(double pool_utilization) {
  210. VLOG(1) << "Client rejects proposal to capture frame (at #"
  211. << next_frame_number_ << ").";
  212. if (capture_size_throttling_mode_ == kThrottlingActive) {
  213. DCHECK(std::isfinite(pool_utilization) && pool_utilization >= 0.0);
  214. const base::TimeTicks timestamp = GetFrameTimestamp(next_frame_number_);
  215. buffer_pool_utilization_.Update(pool_utilization, timestamp);
  216. AnalyzeAndAdjust(timestamp);
  217. }
  218. // Note: Do not advance |next_frame_number_| since it will be re-used for the
  219. // next capture proposal.
  220. }
  221. bool VideoCaptureOracle::CompleteCapture(int frame_number,
  222. bool capture_was_successful,
  223. base::TimeTicks* frame_timestamp) {
  224. num_frames_pending_--;
  225. DCHECK_GE(num_frames_pending_, 0);
  226. // Drop frame if previously delivered frame number is higher.
  227. if (last_successfully_delivered_frame_number_ > frame_number) {
  228. LOG_IF(WARNING, capture_was_successful)
  229. << "Out of order frame delivery detected (have #" << frame_number
  230. << ", last was #" << last_successfully_delivered_frame_number_
  231. << "). Dropping frame.";
  232. return false;
  233. }
  234. if (!IsFrameInRecentHistory(frame_number)) {
  235. LOG(WARNING) << "Very old capture being ignored: frame #" << frame_number;
  236. return false;
  237. }
  238. if (!capture_was_successful) {
  239. VLOG(2) << "Capture of frame #" << frame_number << " was not successful.";
  240. return false;
  241. }
  242. DCHECK_NE(last_successfully_delivered_frame_number_, frame_number);
  243. last_successfully_delivered_frame_number_ = frame_number;
  244. *frame_timestamp = GetFrameTimestamp(frame_number);
  245. // If enabled, log a measurement of how this frame timestamp has incremented
  246. // in relation to an ideal increment.
  247. if (VLOG_IS_ON(3) && frame_number > 0) {
  248. const base::TimeDelta delta =
  249. *frame_timestamp - GetFrameTimestamp(frame_number - 1);
  250. if (content_sampler_.HasProposal()) {
  251. const double estimated_frame_rate =
  252. 1000000.0 / content_sampler_.detected_period().InMicroseconds();
  253. const int rounded_frame_rate =
  254. static_cast<int>(estimated_frame_rate + 0.5);
  255. VLOG_STREAM(3) << base::StringPrintf(
  256. "Captured #%d: delta=%" PRId64
  257. " usec"
  258. ", now locked into {%s}, %+0.1f%% slower than %d FPS",
  259. frame_number, delta.InMicroseconds(),
  260. content_sampler_.detected_region().ToString().c_str(),
  261. 100.0 * FractionFromExpectedFrameRate(delta, rounded_frame_rate),
  262. rounded_frame_rate);
  263. } else {
  264. VLOG_STREAM(3) << base::StringPrintf(
  265. "Captured #%d: delta=%" PRId64
  266. " usec"
  267. ", d/30fps=%+0.1f%%, d/25fps=%+0.1f%%, d/24fps=%+0.1f%%",
  268. frame_number, delta.InMicroseconds(),
  269. 100.0 * FractionFromExpectedFrameRate(delta, 30),
  270. 100.0 * FractionFromExpectedFrameRate(delta, 25),
  271. 100.0 * FractionFromExpectedFrameRate(delta, 24));
  272. }
  273. }
  274. return true;
  275. }
  276. void VideoCaptureOracle::CancelAllCaptures() {
  277. // The following is the desired behavior:
  278. //
  279. // for (int i = num_frames_pending_; i > 0; --i) {
  280. // CompleteCapture(next_frame_number_ - i, false, nullptr);
  281. // --num_frames_pending_;
  282. // }
  283. //
  284. // ...which simplifies to:
  285. num_frames_pending_ = 0;
  286. }
  287. void VideoCaptureOracle::RecordConsumerFeedback(
  288. int frame_number,
  289. const media::VideoCaptureFeedback& feedback) {
  290. // Max frame-rate constraint.
  291. base::TimeDelta period;
  292. if (std::isfinite(feedback.max_framerate_fps) &&
  293. feedback.max_framerate_fps > 0.0) {
  294. period =
  295. std::max(min_capture_period_, base::Hertz(feedback.max_framerate_fps));
  296. } else {
  297. period = min_capture_period_;
  298. }
  299. smoothing_sampler_.SetMinCapturePeriod(period);
  300. content_sampler_.SetMinCapturePeriod(period);
  301. // Max pixels constraint. Only respected if auto-throttling is off because
  302. // consumers could just rescale the image.
  303. if (capture_size_throttling_mode_ != kThrottlingActive) {
  304. int limit;
  305. if (feedback.max_pixels < std::numeric_limits<int>::max()) {
  306. // +1 so that |FindSmallerFrameSize| could return exact |max_pixels| size.
  307. limit = feedback.max_pixels + 1;
  308. } else {
  309. limit = std::numeric_limits<int>::max();
  310. }
  311. int area = resolution_chooser_.FindSmallerFrameSize(limit, 1).GetArea();
  312. resolution_chooser_.SetTargetFrameArea(area);
  313. }
  314. // resource_utilization feedback.
  315. if (capture_size_throttling_mode_ == kThrottlingDisabled)
  316. return;
  317. if (!std::isfinite(feedback.resource_utilization)) {
  318. LOG(DFATAL) << "Non-finite utilization provided by consumer for frame #"
  319. << frame_number << ": " << feedback.resource_utilization;
  320. return;
  321. }
  322. if (feedback.resource_utilization <= 0.0)
  323. return; // Non-positive values are normal, meaning N/A.
  324. if (capture_size_throttling_mode_ != kThrottlingActive) {
  325. VLOG(1) << "Received consumer feedback at frame #" << frame_number
  326. << "; activating capture size auto-throttling.";
  327. capture_size_throttling_mode_ = kThrottlingActive;
  328. }
  329. if (!IsFrameInRecentHistory(frame_number)) {
  330. VLOG(1) << "Very old frame feedback being ignored: frame #" << frame_number;
  331. return;
  332. }
  333. const base::TimeTicks timestamp = GetFrameTimestamp(frame_number);
  334. // Translate the utilization metric to be in terms of the capable frame area
  335. // and update the feedback accumulators. Research suggests utilization is at
  336. // most linearly proportional to area, and typically is sublinear. Either
  337. // way, the end-to-end system should converge to the right place using the
  338. // more-conservative assumption (linear).
  339. const int area_at_full_utilization = base::saturated_cast<int>(
  340. capture_size_.GetArea() / feedback.resource_utilization);
  341. estimated_capable_area_.Update(area_at_full_utilization, timestamp);
  342. }
  343. void VideoCaptureOracle::SetMinSizeChangePeriod(base::TimeDelta period) {
  344. min_size_change_period_ = period;
  345. }
  346. gfx::Size VideoCaptureOracle::capture_size() const {
  347. return capture_size_;
  348. }
  349. // static
  350. const char* VideoCaptureOracle::EventAsString(Event event) {
  351. switch (event) {
  352. case kCompositorUpdate:
  353. return "compositor";
  354. case kRefreshRequest:
  355. return "refresh";
  356. case kRefreshDemand:
  357. return "demand";
  358. case kNumEvents:
  359. break;
  360. }
  361. NOTREACHED();
  362. return "unknown";
  363. }
  364. base::TimeTicks VideoCaptureOracle::GetFrameTimestamp(int frame_number) const {
  365. DCHECK(IsFrameInRecentHistory(frame_number));
  366. return frame_timestamps_[frame_number % kMaxFrameTimestamps];
  367. }
  368. void VideoCaptureOracle::SetFrameTimestamp(int frame_number,
  369. base::TimeTicks timestamp) {
  370. DCHECK(IsFrameInRecentHistory(frame_number));
  371. frame_timestamps_[frame_number % kMaxFrameTimestamps] = timestamp;
  372. }
  373. NOINLINE bool VideoCaptureOracle::IsFrameInRecentHistory(
  374. int frame_number) const {
  375. // Adding (next_frame_number_ >= 0) helps the compiler deduce that there
  376. // is no possibility of overflow here. NOINLINE is also required to ensure the
  377. // compiler can make this deduction (some compilers fail to otherwise...).
  378. return (frame_number >= 0 && next_frame_number_ >= 0 &&
  379. frame_number <= next_frame_number_ &&
  380. (next_frame_number_ - frame_number) < kMaxFrameTimestamps);
  381. }
  382. void VideoCaptureOracle::CommitCaptureSizeAndReset(
  383. base::TimeTicks last_frame_time) {
  384. capture_size_ = resolution_chooser_.capture_size();
  385. VLOG(2) << "Now proposing a capture size of " << capture_size_.ToString();
  386. // Reset each short-term feedback accumulator with a stable-state starting
  387. // value.
  388. const base::TimeTicks ignore_before_time = JustAfter(last_frame_time);
  389. buffer_pool_utilization_.Reset(1.0, ignore_before_time);
  390. estimated_capable_area_.Reset(capture_size_.GetArea(), ignore_before_time);
  391. }
  392. void VideoCaptureOracle::AnalyzeAndAdjust(const base::TimeTicks analyze_time) {
  393. DCHECK(capture_size_throttling_mode_ == kThrottlingActive);
  394. const int decreased_area = AnalyzeForDecreasedArea(analyze_time);
  395. if (decreased_area > 0) {
  396. resolution_chooser_.SetTargetFrameArea(decreased_area);
  397. if (!emit_log_message_cb_.is_null()) {
  398. emit_log_message_cb_.Run(base::StringPrintf(
  399. "VFC: CaptureOracle - Decreasing resolution. "
  400. "buffer_utilization_: %lf "
  401. "estimated_cappable_area: %lf "
  402. "capture_size: %s ",
  403. buffer_pool_utilization_.current(), estimated_capable_area_.current(),
  404. capture_size_.ToString().c_str()));
  405. }
  406. return;
  407. }
  408. const int increased_area = AnalyzeForIncreasedArea(analyze_time);
  409. if (increased_area > 0) {
  410. resolution_chooser_.SetTargetFrameArea(increased_area);
  411. return;
  412. }
  413. // Explicitly set the target frame area to the current capture area. This
  414. // cancels-out the results of a previous call to this method, where the
  415. // |resolution_chooser_| may have been instructed to increase or decrease the
  416. // capture size. Conditions may have changed since then which indicate no
  417. // change should be committed (via CommitCaptureSizeAndReset()).
  418. resolution_chooser_.SetTargetFrameArea(capture_size_.GetArea());
  419. }
  420. int VideoCaptureOracle::AnalyzeForDecreasedArea(base::TimeTicks analyze_time) {
  421. const int current_area = capture_size_.GetArea();
  422. DCHECK_GT(current_area, 0);
  423. // Translate the recent-average buffer pool utilization to be in terms of
  424. // "capable number of pixels per frame," for an apples-to-apples comparison
  425. // below.
  426. int buffer_capable_area;
  427. if (HasSufficientRecentFeedback(buffer_pool_utilization_, analyze_time) &&
  428. buffer_pool_utilization_.current() > 1.0) {
  429. // This calculation is hand-wavy, but seems to work well in a variety of
  430. // situations.
  431. buffer_capable_area =
  432. static_cast<int>(current_area / buffer_pool_utilization_.current());
  433. } else {
  434. buffer_capable_area = current_area;
  435. }
  436. int consumer_capable_area;
  437. if (HasSufficientRecentFeedback(estimated_capable_area_, analyze_time)) {
  438. consumer_capable_area =
  439. base::saturated_cast<int>(estimated_capable_area_.current());
  440. } else {
  441. consumer_capable_area = current_area;
  442. }
  443. // If either of the "capable areas" is less than the current capture area,
  444. // decrease the capture area by AT LEAST one step.
  445. int decreased_area = -1;
  446. const int capable_area = std::min(buffer_capable_area, consumer_capable_area);
  447. if (capable_area < current_area) {
  448. decreased_area = std::min(
  449. capable_area,
  450. resolution_chooser_.FindSmallerFrameSize(current_area, 1).GetArea());
  451. VLOG_IF(2, !start_time_of_underutilization_.is_null())
  452. << "Contiguous period of under-utilization ends: "
  453. "System is suddenly over-utilized.";
  454. start_time_of_underutilization_ = base::TimeTicks();
  455. VLOG(2) << "Proposing a "
  456. << (100.0 * (current_area - decreased_area) / current_area)
  457. << "% decrease in capture area. :-(";
  458. }
  459. // Always log the capability interpretations at verbose logging level 3. At
  460. // level 2, only log when when proposing a decreased area.
  461. VLOG(decreased_area == -1 ? 3 : 2)
  462. << "Capability of pool=" << (100.0 * buffer_capable_area / current_area)
  463. << "%, consumer=" << (100.0 * consumer_capable_area / current_area)
  464. << '%';
  465. return decreased_area;
  466. }
  467. int VideoCaptureOracle::AnalyzeForIncreasedArea(base::TimeTicks analyze_time) {
  468. // Compute what one step up in capture size/area would be. If the current
  469. // area is already at the maximum, no further analysis is necessary.
  470. const int current_area = capture_size_.GetArea();
  471. const int increased_area =
  472. resolution_chooser_.FindLargerFrameSize(current_area, 1).GetArea();
  473. if (increased_area <= current_area)
  474. return -1;
  475. // Determine whether the buffer pool could handle an increase in area.
  476. if (!HasSufficientRecentFeedback(buffer_pool_utilization_, analyze_time))
  477. return -1;
  478. if (buffer_pool_utilization_.current() > 0.0) {
  479. const int buffer_capable_area = base::saturated_cast<int>(
  480. current_area / buffer_pool_utilization_.current());
  481. if (buffer_capable_area < increased_area) {
  482. VLOG_IF(2, !start_time_of_underutilization_.is_null())
  483. << "Contiguous period of under-utilization ends: "
  484. "Buffer pool is no longer under-utilized.";
  485. start_time_of_underutilization_ = base::TimeTicks();
  486. return -1; // Buffer pool is not under-utilized.
  487. }
  488. }
  489. // Determine whether the consumer could handle an increase in area.
  490. if (HasSufficientRecentFeedback(estimated_capable_area_, analyze_time)) {
  491. if (estimated_capable_area_.current() < increased_area) {
  492. VLOG_IF(2, !start_time_of_underutilization_.is_null())
  493. << "Contiguous period of under-utilization ends: "
  494. "Consumer is no longer under-utilized.";
  495. start_time_of_underutilization_ = base::TimeTicks();
  496. return -1; // Consumer is not under-utilized.
  497. }
  498. } else if (estimated_capable_area_.update_time() ==
  499. estimated_capable_area_.reset_time()) {
  500. // The consumer does not provide any feedback. In this case, the consumer's
  501. // capability isn't a consideration.
  502. } else {
  503. // Consumer is providing feedback, but hasn't reported it recently. Just in
  504. // case it's stalled, don't make things worse by increasing the capture
  505. // area.
  506. return -1;
  507. }
  508. // At this point, the system is currently under-utilized. Reset the start
  509. // time if the system was not under-utilized when the last analysis was made.
  510. if (start_time_of_underutilization_.is_null())
  511. start_time_of_underutilization_ = analyze_time;
  512. // If the under-utilization started soon after the last source size change,
  513. // permit an immediate increase in the capture area. This allows the system
  514. // to quickly step-up to an ideal point.
  515. if (start_time_of_underutilization_ - source_size_change_time_ <=
  516. GetExplorationPeriodAfterSourceSizeChange()) {
  517. VLOG(2) << "Proposing a "
  518. << (100.0 * (increased_area - current_area) / current_area)
  519. << "% increase in capture area after source size change. :-)";
  520. return increased_area;
  521. }
  522. // While content is animating, require a "proving period" of contiguous
  523. // under-utilization before increasing the capture area. This will mitigate
  524. // the risk of frames getting dropped when the data volume increases.
  525. if ((analyze_time - last_time_animation_was_detected_) <
  526. kDebouncingPeriodForAnimatedContent) {
  527. if ((analyze_time - start_time_of_underutilization_) <
  528. kProvingPeriodForAnimatedContent) {
  529. // Content is animating but the system needs to be under-utilized for a
  530. // longer period of time.
  531. return -1;
  532. } else {
  533. // Content is animating and the system has been contiguously
  534. // under-utilized for a good long time.
  535. VLOG(2) << "Proposing a *cautious* "
  536. << (100.0 * (increased_area - current_area) / current_area)
  537. << "% increase in capture area while content is animating. :-)";
  538. // Reset the "proving period."
  539. start_time_of_underutilization_ = base::TimeTicks();
  540. return increased_area;
  541. }
  542. }
  543. // Content is not animating, so permit an immediate increase in the capture
  544. // area. This allows the system to quickly improve the quality of
  545. // non-animating content (frame drops are not much of a concern).
  546. VLOG(2) << "Proposing a "
  547. << (100.0 * (increased_area - current_area) / current_area)
  548. << "% increase in capture area for non-animating content. :-)";
  549. return increased_area;
  550. }
  551. base::TimeDelta
  552. VideoCaptureOracle::GetExplorationPeriodAfterSourceSizeChange() {
  553. return 3 * min_size_change_period_;
  554. }
  555. bool VideoCaptureOracle::HasSufficientRecentFeedback(
  556. const FeedbackSignalAccumulator<base::TimeTicks>& accumulator,
  557. base::TimeTicks now) {
  558. const base::TimeDelta amount_of_history =
  559. accumulator.update_time() - accumulator.reset_time();
  560. return (amount_of_history >= min_size_change_period_) &&
  561. (now - accumulator.update_time() <= kMaxTimeSinceLastFeedbackUpdate);
  562. }
  563. void VideoCaptureOracle::SetLogCallback(
  564. base::RepeatingCallback<void(const std::string&)> emit_log_cb) {
  565. emit_log_message_cb_ = std::move(emit_log_cb);
  566. }
  567. } // namespace media