Skip to content

Commit

Permalink
Predict smooth from all existing data per codec
Browse files Browse the repository at this point in the history
Use all existing data to predict if smooth is true or false for a
specific configuration. For example, 4K smooth implies that all
lower resolutions are smooth as well. Similarly, HD not smooth
implies that all higher resolutions are not smooth as well.

Bug: chromium:1187565, chromium:1308865
Change-Id: I55f2b6fb9c8c4fc06616558e303ba49c2092737f
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3512656
Reviewed-by: Will Cassella <cassew@chromium.org>
Commit-Queue: Johannes Kron <kron@chromium.org>
Cr-Commit-Position: refs/heads/main@{#985272}
  • Loading branch information
Johannes Kron authored and Chromium LUCI CQ committed Mar 25, 2022
1 parent 32f4943 commit 58bd6ff
Show file tree
Hide file tree
Showing 10 changed files with 659 additions and 64 deletions.
31 changes: 28 additions & 3 deletions media/capabilities/webrtc_video_stats_db.cc
Expand Up @@ -7,6 +7,7 @@
#include "base/check_op.h"
#include "base/format_macros.h"
#include "base/metrics/field_trial_params.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/stringprintf.h"
#include "media/base/media_switches.h"
#include "media/capabilities/bucket_utility.h"
Expand Down Expand Up @@ -65,10 +66,34 @@ std::string WebrtcVideoStatsDB::VideoDescKey::Serialize() const {
return video_part;
}

std::string WebrtcVideoStatsDB::VideoDescKey::SerializeWithoutPixels() const {
std::string video_part =
base::StringPrintf("%d|%d|%d|", is_decode_stats,
static_cast<int>(codec_profile), hardware_accelerated);

return video_part;
}

std::string WebrtcVideoStatsDB::VideoDescKey::ToLogStringForDebug() const {
return "Key {" + Serialize() + "}";
}

// static
absl::optional<int> WebrtcVideoStatsDB::VideoDescKey::ParsePixelsFromKey(
std::string key) {
constexpr size_t kMinimumIndexOfLastSeparator = 5;
size_t last_separator_index = key.rfind("|");
if (last_separator_index != std::string::npos &&
last_separator_index >= kMinimumIndexOfLastSeparator &&
(last_separator_index + 1) < key.size()) {
int parsed_pixels;
if (base::StringToInt(&key.c_str()[last_separator_index + 1],
&parsed_pixels))
return parsed_pixels;
}
return absl::nullopt;
}

WebrtcVideoStatsDB::VideoStats::VideoStats(double timestamp,
uint32_t frames_processed,
uint32_t key_frames_processed,
Expand Down Expand Up @@ -127,10 +152,10 @@ bool operator!=(const WebrtcVideoStatsDB::VideoStats& x,
}

// static
int WebrtcVideoStatsDB::GetMaxDaysToKeepStats() {
return base::GetFieldTrialParamByFeatureAsInt(
base::TimeDelta WebrtcVideoStatsDB::GetMaxTimeToKeepStats() {
return base::Days(base::GetFieldTrialParamByFeatureAsInt(
kWebrtcMediaCapabilitiesParameters, kMaxDaysToKeepStatsParamName,
kMaxDaysToKeepStatsDefault);
kMaxDaysToKeepStatsDefault));
}

// static
Expand Down
33 changes: 29 additions & 4 deletions media/capabilities/webrtc_video_stats_db.h
Expand Up @@ -10,8 +10,11 @@

#include "base/callback_forward.h"
#include "base/check.h"
#include "base/containers/flat_map.h"
#include "base/time/time.h"
#include "media/base/media_export.h"
#include "media/base/video_codecs.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
#include "ui/gfx/geometry/size.h"

namespace media {
Expand All @@ -31,9 +34,15 @@ class MEDIA_EXPORT WebrtcVideoStatsDB {
// Returns a concise string representation of the key for storing in DB.
std::string Serialize() const;

// Returns a concise string representation of the key without pixels for
// querying the DB.
std::string SerializeWithoutPixels() const;

// For debug logging. NOT interchangeable with Serialize().
std::string ToLogStringForDebug() const;

static absl::optional<int> ParsePixelsFromKey(std::string key);

// Note: operator == and != are defined outside this class.
const bool is_decode_stats;
const VideoCodecProfile codec_profile;
Expand Down Expand Up @@ -73,6 +82,12 @@ class MEDIA_EXPORT WebrtcVideoStatsDB {
// |VideoDescKey|.
using VideoStatsEntry = std::vector<VideoStats>;

// VideoStatsCollection is used to return a collection of entries
// corresponding to a certain key except for the number of pixels used. The
// number of pixels is instead used as a key in the map for each
// VideoStatsEntry.
using VideoStatsCollection = base::flat_map<int, VideoStatsEntry>;

virtual ~WebrtcVideoStatsDB() = default;

// The maximum number of pixels that a stats key can have without being
Expand Down Expand Up @@ -108,7 +123,7 @@ class MEDIA_EXPORT WebrtcVideoStatsDB {
// Number of days after which a stats entry will be discarded. This
// avoids users getting stuck with a bad capability prediction that may have
// been due to one-off circumstances.
static int GetMaxDaysToKeepStats();
static base::TimeDelta GetMaxTimeToKeepStats();

// Run asynchronous initialization of database. Initialization must complete
// before calling other APIs. |init_cb| must not be
Expand All @@ -126,14 +141,24 @@ class MEDIA_EXPORT WebrtcVideoStatsDB {
const VideoStats& video_stats,
AppendVideoStatsCB append_done_cb) = 0;

// Returns the stats associated with `key`. The `get_stats_cb` will receive
// Returns the stats associated with `key`. The `get_stats_cb` will receive
// the stats in addition to a boolean signaling if the call was successful.
// VideoStatsEntry can be nullptr if there was no data associated with `key`.
// VideoStatsEntry can be nullopt if there was no data associated with `key`.
using GetVideoStatsCB =
base::OnceCallback<void(bool, std::unique_ptr<VideoStatsEntry>)>;
base::OnceCallback<void(bool, absl::optional<VideoStatsEntry>)>;
virtual void GetVideoStats(const VideoDescKey& key,
GetVideoStatsCB get_stats_cb) = 0;

// Returns a collection of stats associated with `key` disregarding pixels.
// The `get_stats_cb` will receive the stats in addition to a boolean
// signaling if the call was successful. VideoStatsEntry can be nullopt if
// there was no data associated with `key`.
using GetVideoStatsCollectionCB =
base::OnceCallback<void(bool, absl::optional<VideoStatsCollection>)>;
virtual void GetVideoStatsCollection(
const VideoDescKey& key,
GetVideoStatsCollectionCB get_stats_cb) = 0;

// Clear all statistics from the DB.
virtual void ClearStats(base::OnceClosure clear_done_cb) = 0;
};
Expand Down
98 changes: 88 additions & 10 deletions media/capabilities/webrtc_video_stats_db_impl.cc
Expand Up @@ -4,7 +4,6 @@

#include "media/capabilities/webrtc_video_stats_db_impl.h"

#include <iostream>
#include <memory>
#include <string>
#include <tuple>
Expand All @@ -18,6 +17,7 @@
#include "base/metrics/histogram_functions.h"
#include "base/metrics/histogram_macros.h"
#include "base/sequence_checker.h"
#include "base/strings/string_util.h"
#include "base/task/post_task.h"
#include "base/task/thread_pool.h"
#include "base/threading/thread_task_runner_handle.h"
Expand Down Expand Up @@ -210,6 +210,38 @@ void WebrtcVideoStatsDBImpl::GetVideoStats(const VideoDescKey& key,
std::move(get_stats_cb)));
}

void WebrtcVideoStatsDBImpl::GetVideoStatsCollection(
const VideoDescKey& key,
GetVideoStatsCollectionCB get_stats_cb) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(IsInitialized());

DVLOG(3) << __func__ << " " << key.ToLogStringForDebug();

// Filter out all entries starting as the serialized key without pixels. This
// corresponds to all entries with the same codec profile, hardware
// accelerate, and decode/encode.
std::string key_without_pixels = key.SerializeWithoutPixels();
auto key_iterator_controller = base::BindRepeating(
[](const std::string& key_filter, const std::string& key) {
if (base::StartsWith(key, key_filter)) {
// Include this entry and continue the search if the key has the
// same beginning as `key_without_pixels`.
return leveldb_proto::Enums::kLoadAndContinue;
} else {
// Cancel otherwise.
return leveldb_proto::Enums::kSkipAndStop;
}
},
key_without_pixels);

db_->LoadKeysAndEntriesWhile(
key_without_pixels, key_iterator_controller,
base::BindOnce(&WebrtcVideoStatsDBImpl::OnGotVideoStatsCollection,
weak_ptr_factory_.GetWeakPtr(), StartPendingOp("Read"),
std::move(get_stats_cb)));
}

bool WebrtcVideoStatsDBImpl::AreStatsValid(
const WebrtcVideoStatsEntryProto* const stats_proto) {
// Check for corruption.
Expand Down Expand Up @@ -281,17 +313,17 @@ void WebrtcVideoStatsDBImpl::WriteUpdatedEntry(
<< new_stats->p99_processing_time_ms();

// Append existing entries.
const int kMaxDaysToKeepStats = GetMaxDaysToKeepStats();
const int kMaxEntriesPerConfig = GetMaxEntriesPerConfig();
DCHECK_GT(kMaxDaysToKeepStats, 0);
const base::TimeDelta max_time_to_keep_stats = GetMaxTimeToKeepStats();
const int max_entries_per_config = GetMaxEntriesPerConfig();
DCHECK_GT(max_time_to_keep_stats, base::Days(0));
double previous_timestamp = new_stats->timestamp();
for (auto const& existing_stats : existing_entry_proto->stats()) {
// Discard existing stats that have expired, if the entry is full, or if the
// timestamps come in the wrong order.
if (wall_clock_->Now() -
base::Time::FromJsTime(existing_stats.timestamp()) <=
base::Days(kMaxDaysToKeepStats) &&
new_entry_proto.stats_size() < kMaxEntriesPerConfig &&
max_time_to_keep_stats &&
new_entry_proto.stats_size() < max_entries_per_config &&
existing_stats.timestamp() < previous_timestamp) {
previous_timestamp = existing_stats.timestamp();
media::WebrtcVideoStatsProto* stats = new_entry_proto.add_stats();
Expand Down Expand Up @@ -342,14 +374,14 @@ void WebrtcVideoStatsDBImpl::OnGotVideoStats(
UMA_HISTOGRAM_BOOLEAN("Media.WebrtcVideoStatsDB.OpSuccess.Read", success);

// Convert from WebrtcVideoStatsEntryProto to VideoStatsEntry.
std::unique_ptr<VideoStatsEntry> entry;
absl::optional<VideoStatsEntry> entry;
if (stats_proto && AreStatsValid(stats_proto.get())) {
DCHECK(success);
const int kMaxDaysToKeepStats = GetMaxDaysToKeepStats();
entry = std::make_unique<VideoStatsEntry>();
const base::TimeDelta max_time_to_keep_stats = GetMaxTimeToKeepStats();
entry.emplace();
for (auto const& stats : stats_proto->stats()) {
if (wall_clock_->Now() - base::Time::FromJsTime(stats.timestamp()) <=
base::Days(kMaxDaysToKeepStats)) {
max_time_to_keep_stats) {
entry->emplace_back(stats.timestamp(), stats.frames_processed(),
stats.key_frames_processed(),
stats.p99_processing_time_ms());
Expand All @@ -365,6 +397,52 @@ void WebrtcVideoStatsDBImpl::OnGotVideoStats(
std::move(get_stats_cb).Run(success, std::move(entry));
}

void WebrtcVideoStatsDBImpl::OnGotVideoStatsCollection(
PendingOpId op_id,
GetVideoStatsCollectionCB get_stats_cb,
bool success,
std::unique_ptr<std::map<std::string, WebrtcVideoStatsEntryProto>>
stats_proto_collection) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DVLOG(3) << __func__ << " get " << (success ? "succeeded" : "FAILED!");
CompletePendingOp(op_id);
UMA_HISTOGRAM_BOOLEAN("Media.WebrtcVideoStatsDB.OpSuccess.Read", success);
// Convert from map of WebrtcVideoStatsEntryProto to VideoStatsCollection.
absl::optional<VideoStatsCollection> collection;
if (stats_proto_collection) {
DCHECK(success);
collection.emplace();
const base::TimeDelta max_time_to_keep_stats = GetMaxTimeToKeepStats();

for (auto const& stats_proto : *stats_proto_collection) {
if (AreStatsValid(&stats_proto.second)) {
VideoStatsEntry entry;
for (auto const& stats : stats_proto.second.stats()) {
if (wall_clock_->Now() - base::Time::FromJsTime(stats.timestamp()) <=
max_time_to_keep_stats) {
entry.emplace_back(stats.timestamp(), stats.frames_processed(),
stats.key_frames_processed(),
stats.p99_processing_time_ms());
}
}

if (!entry.empty()) {
absl::optional<int> pixels =
VideoDescKey::ParsePixelsFromKey(stats_proto.first);
if (pixels) {
collection->insert({*pixels, std::move(entry)});
}
}
}
}
if (collection->empty()) {
collection.reset();
}
}

std::move(get_stats_cb).Run(success, std::move(collection));
}

void WebrtcVideoStatsDBImpl::ClearStats(base::OnceClosure clear_done_cb) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DVLOG(2) << __func__;
Expand Down
13 changes: 13 additions & 0 deletions media/capabilities/webrtc_video_stats_db_impl.h
Expand Up @@ -54,6 +54,8 @@ class MEDIA_EXPORT WebrtcVideoStatsDBImpl : public WebrtcVideoStatsDB {
AppendVideoStatsCB append_done_cb) override;
void GetVideoStats(const VideoDescKey& key,
GetVideoStatsCB get_stats_cb) override;
void GetVideoStatsCollection(const VideoDescKey& key,
GetVideoStatsCollectionCB get_stats_cb) override;
void ClearStats(base::OnceClosure clear_done_cb) override;

private:
Expand Down Expand Up @@ -144,6 +146,17 @@ class MEDIA_EXPORT WebrtcVideoStatsDBImpl : public WebrtcVideoStatsDB {
bool success,
std::unique_ptr<WebrtcVideoStatsEntryProto> stats_proto);

// Called when GetVideoStatsCollection() operation was performed.
// `get_stats_cb` will be run with `success` and a `VideoStatsCollection`
// created from the `stats_proto` map or nullptr if no entries were found for
// the filtered key.
void OnGotVideoStatsCollection(
PendingOpId op_id,
GetVideoStatsCollectionCB get_stats_cb,
bool success,
std::unique_ptr<std::map<std::string, WebrtcVideoStatsEntryProto>>
stats_proto);

// Internal callback for OnLoadAllKeysForClearing(), initially triggered by
// ClearStats(). Method simply logs `success` and runs `clear_done_cb`.
void OnStatsCleared(PendingOpId op_id,
Expand Down

0 comments on commit 58bd6ff

Please sign in to comment.