summaryrefslogtreecommitdiff
path: root/chromium/third_party/blink/renderer/platform/media/smoothness_helper.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/third_party/blink/renderer/platform/media/smoothness_helper.cc')
-rw-r--r--chromium/third_party/blink/renderer/platform/media/smoothness_helper.cc240
1 files changed, 240 insertions, 0 deletions
diff --git a/chromium/third_party/blink/renderer/platform/media/smoothness_helper.cc b/chromium/third_party/blink/renderer/platform/media/smoothness_helper.cc
new file mode 100644
index 00000000000..f8ede690c20
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/media/smoothness_helper.cc
@@ -0,0 +1,240 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/public/platform/media/smoothness_helper.h"
+
+#include "base/bind.h"
+#include "base/timer/timer.h"
+#include "base/unguessable_token.h"
+#include "media/learning/common/learning_task_controller.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+
+namespace blink {
+namespace {
+
+using ::media::learning::FeatureVector;
+using ::media::learning::LearningTaskController;
+using ::media::learning::TargetValue;
+
+static constexpr base::TimeDelta kSegmentSize =
+ base::TimeDelta::FromSeconds(5);
+
+// Maximum distance between NNRs for them to be consecutive.
+static constexpr base::TimeDelta kMaxNNRDistance =
+ base::TimeDelta::FromSeconds(60);
+
+// Max proportion of dropped frames in a window before we call it "not smooth".
+static constexpr float kMaxDroppedFramesPerWindow = 0.2;
+
+} // namespace
+
+// Monitor smoothness during a playback, and call back on each window.
+class SmoothnessWindowMonitor {
+ public:
+ using WindowCB = base::RepeatingCallback<void(int64_t dropped_frames,
+ int64_t decoded_frames)>;
+ SmoothnessWindowMonitor(SmoothnessHelper::Client* player, WindowCB cb)
+ : player_(player), cb_(std::move(cb)) {
+ segment_dropped_frames_ = player_->DroppedFrameCount();
+ segment_decoded_frames_ = player_->DecodedFrameCount();
+
+ update_timer_.Start(FROM_HERE, kSegmentSize,
+ base::BindRepeating(&SmoothnessWindowMonitor::OnTimer,
+ base::Unretained(this)));
+ }
+
+ ~SmoothnessWindowMonitor() = default;
+
+ // Split playback into segments of length |kSegmentSize|, and update the
+ // default value of the current playback.
+ void OnTimer() {
+ auto new_dropped_frames = player_->DroppedFrameCount();
+ auto dropped_frames = new_dropped_frames - segment_dropped_frames_;
+ segment_dropped_frames_ = new_dropped_frames;
+
+ auto new_decoded_frames = player_->DecodedFrameCount();
+ auto decoded_frames = new_decoded_frames - segment_decoded_frames_;
+ segment_decoded_frames_ = new_decoded_frames;
+
+ if (!decoded_frames)
+ return;
+
+ cb_.Run(dropped_frames, decoded_frames);
+ }
+
+ private:
+ SmoothnessHelper::Client* player_ = nullptr;
+ WindowCB cb_;
+ base::RepeatingTimer update_timer_;
+ // Current dropped, decoded frames at the start of the segment.
+ int64_t segment_decoded_frames_;
+ int64_t segment_dropped_frames_;
+};
+
+SmoothnessHelper::SmoothnessHelper(const FeatureVector& features)
+ : features_(features) {}
+
+SmoothnessHelper::~SmoothnessHelper() = default;
+
+class SmoothnessHelperImpl : public SmoothnessHelper {
+ public:
+ SmoothnessHelperImpl(
+ std::unique_ptr<LearningTaskController> consecutive_controller,
+ std::unique_ptr<LearningTaskController> nnr_controller,
+ const FeatureVector& features,
+ Client* player)
+ : SmoothnessHelper(features),
+ consecutive_bad_(std::move(consecutive_controller)),
+ consecutive_nnr_(std::move(nnr_controller)),
+ player_(player) {
+ monitor_ = std::make_unique<SmoothnessWindowMonitor>(
+ player_, base::BindRepeating(&SmoothnessHelperImpl::OnWindow,
+ base::Unretained(this)));
+ }
+
+ // This will ignore the last segment, if any, which is fine since it's not
+ // a complete segment. However, any in-progress observation will be completed
+ // with the default value if we've gotten enough data to set one.
+ ~SmoothnessHelperImpl() override = default;
+
+ // See if we've exceeded the intra-NNR distance, and reset everything. Note
+ // that this can be called even when there isn't an NNR.
+ void UpdateNNRWindow() {
+ if (!most_recent_nnr_)
+ return;
+
+ auto now = base::TimeTicks::Now();
+ auto delta = now - *most_recent_nnr_;
+ if (delta >= kMaxNNRDistance) {
+ most_recent_nnr_.reset();
+ num_consecutive_nnrs_ = 0;
+ }
+ }
+
+ void NotifyNNR() override {
+ UpdateNNRWindow();
+ most_recent_nnr_ = base::TimeTicks::Now();
+ num_consecutive_nnrs_++;
+
+ if (num_consecutive_nnrs_ > max_num_consecutive_nnrs_) {
+ max_num_consecutive_nnrs_ = num_consecutive_nnrs_;
+
+ // Insist that we've started the NNR instance, so that we enforce a
+ // minimum amount of playback time before recording anything. Though
+ // it's possible that an NNR is interesting enough to record it anyway,
+ // and we only want to elide zero-NNR observations for short playbacks.
+ if (consecutive_nnr_.is_started()) {
+ consecutive_nnr_.UpdateObservation(
+ features(), TargetValue(max_num_consecutive_nnrs_));
+ }
+ }
+ }
+
+ // Split playback into segments of length |kSegmentSize|, and update the
+ // default value of the current playback.
+ void OnWindow(int64_t dropped_frames, int64_t decoded_frames) {
+ // After the first window, start the NNR observation. We want to ignore any
+ // short playback windows. We might want to require more than one window.
+ // TODO(liberato): How many windows count as a playback for NNR?
+ if (!consecutive_nnr_.is_started()) {
+ UpdateNNRWindow();
+ consecutive_nnr_.UpdateObservation(
+ features(), TargetValue(max_num_consecutive_nnrs_));
+ }
+
+ // Compute the percentage of dropped frames for this window.
+ double pct = (static_cast<double>(dropped_frames)) / decoded_frames;
+
+ // Once we get one full window, default to 0 for the consecutive windows
+ // prediction task.
+ if (!consecutive_bad_.is_started())
+ consecutive_bad_.UpdateObservation(features(), TargetValue(0));
+
+ // If this is a bad window, extend the run of consecutive bad windows, and
+ // update the target value if this is a new longest run.
+ if (pct >= kMaxDroppedFramesPerWindow) {
+ consecutive_bad_windows_++;
+ if (consecutive_bad_windows_ > max_consecutive_bad_windows_) {
+ max_consecutive_bad_windows_ = consecutive_bad_windows_;
+ consecutive_bad_.UpdateObservation(
+ features(), TargetValue(max_consecutive_bad_windows_));
+ }
+ } else {
+ consecutive_bad_windows_ = 0;
+ // Don't update the target value, since any previous target value is still
+ // the max consecutive windows.
+ }
+ }
+
+ // Helper for different learning tasks.
+ struct Task {
+ Task(std::unique_ptr<LearningTaskController> controller)
+ : controller_(std::move(controller)) {}
+
+ Task(const Task&) = delete;
+ Task& operator=(const Task&) = delete;
+ ~Task() = default;
+
+ // Return true if and only if we've started an observation.
+ bool is_started() const { return !!id_; }
+
+ void UpdateObservation(const FeatureVector& features,
+ TargetValue current_target) {
+ target_value_ = current_target;
+ if (!is_started()) {
+ id_ = base::UnguessableToken::Create();
+ controller_->BeginObservation(*id_, features, target_value_);
+ } else {
+ controller_->UpdateDefaultTarget(*id_, target_value_);
+ }
+ }
+
+ const TargetValue& target_value() const { return target_value_; }
+
+ private:
+ // If an observation is in progress, then this is the id.
+ absl::optional<base::UnguessableToken> id_;
+ std::unique_ptr<LearningTaskController> controller_;
+ TargetValue target_value_;
+ };
+
+ // Struct to hold all of the "at least |n| consecutive bad windows" data.
+ struct Task consecutive_bad_;
+
+ int consecutive_bad_windows_ = 0;
+ int max_consecutive_bad_windows_ = 0;
+
+ struct Task consecutive_nnr_;
+
+ // Time of the most recent nnr.
+ absl::optional<base::TimeTicks> most_recent_nnr_;
+
+ // Number of NNRs that have occurred within |kMaxNNRDistance|.
+ int num_consecutive_nnrs_ = 0;
+
+ // Maximum value of |num_consecutive_nnrs_| that we've observed.
+ int max_num_consecutive_nnrs_ = 0;
+
+ // WebMediaPlayer which will tell us about the decoded / dropped frame counts.
+ Client* player_;
+
+ std::unique_ptr<SmoothnessWindowMonitor> monitor_;
+};
+
+// static
+std::unique_ptr<SmoothnessHelper> SmoothnessHelper::Create(
+ std::unique_ptr<LearningTaskController> bad_controller,
+ std::unique_ptr<LearningTaskController> nnr_controller,
+ const FeatureVector& features,
+ Client* player) {
+ return std::make_unique<SmoothnessHelperImpl>(
+ std::move(bad_controller), std::move(nnr_controller), features, player);
+}
+
+// static
+base::TimeDelta SmoothnessHelper::SegmentSizeForTesting() {
+ return kSegmentSize;
+}
+
+} // namespace blink