blob: 37a13a4a9bba22c3d892105c6058ea9bdd301f62 [file] [log] [blame]
// Copyright 2017 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 "media/capabilities/video_decode_stats_db_impl.h"
#include <memory>
#include <tuple>
#include "base/files/file_path.h"
#include "base/logging.h"
#include "base/metrics/histogram_macros.h"
#include "base/sequence_checker.h"
#include "base/task/post_task.h"
#include "components/leveldb_proto/proto_database_impl.h"
#include "media/capabilities/video_decode_stats.pb.h"
namespace media {
namespace {
// Avoid changing client name. Used in UMA.
// See comments in components/leveldb_proto/leveldb_database.h
const char kDatabaseClientName[] = "VideoDecodeStatsDB";
}; // namespace
VideoDecodeStatsDBImplFactory::VideoDecodeStatsDBImplFactory(
base::FilePath db_dir)
: db_dir_(db_dir) {
DVLOG(2) << __func__ << " db_dir:" << db_dir_;
}
VideoDecodeStatsDBImplFactory::~VideoDecodeStatsDBImplFactory() = default;
std::unique_ptr<VideoDecodeStatsDB> VideoDecodeStatsDBImplFactory::CreateDB() {
std::unique_ptr<leveldb_proto::ProtoDatabase<DecodeStatsProto>> db_;
auto proto_db =
std::make_unique<leveldb_proto::ProtoDatabaseImpl<DecodeStatsProto>>(
base::CreateSequencedTaskRunnerWithTraits(
{base::MayBlock(), base::TaskPriority::BEST_EFFORT,
base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN}));
return std::make_unique<VideoDecodeStatsDBImpl>(std::move(proto_db), db_dir_);
}
VideoDecodeStatsDBImpl::VideoDecodeStatsDBImpl(
std::unique_ptr<leveldb_proto::ProtoDatabase<DecodeStatsProto>> db,
const base::FilePath& db_dir)
: db_(std::move(db)), db_dir_(db_dir), weak_ptr_factory_(this) {
DCHECK(db_);
DCHECK(!db_dir_.empty());
}
VideoDecodeStatsDBImpl::~VideoDecodeStatsDBImpl() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
}
void VideoDecodeStatsDBImpl::Initialize(InitializeCB init_cb) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(init_cb);
DCHECK(!IsInitialized());
DCHECK(!db_destroy_pending_);
// "Simple options" will use the default global cache of 8MB. In the worst
// case our whole DB will be less than 35K, so we aren't worried about
// spamming the cache.
// TODO(chcunningham): Keep an eye on the size as the table evolves.
db_->Init(kDatabaseClientName, db_dir_, leveldb_proto::CreateSimpleOptions(),
base::BindOnce(&VideoDecodeStatsDBImpl::OnInit,
weak_ptr_factory_.GetWeakPtr(), std::move(init_cb)));
}
void VideoDecodeStatsDBImpl::OnInit(InitializeCB init_cb, bool success) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DVLOG(2) << __func__ << (success ? " succeeded" : " FAILED!");
UMA_HISTOGRAM_BOOLEAN("Media.VideoDecodeStatsDB.OpSuccess.Initialize",
success);
db_init_ = true;
// Can't use DB when initialization fails.
if (!success)
db_.reset();
std::move(init_cb).Run(success);
}
bool VideoDecodeStatsDBImpl::IsInitialized() {
// |db_| will be null if Initialization failed.
return db_init_ && db_;
}
void VideoDecodeStatsDBImpl::AppendDecodeStats(
const VideoDescKey& key,
const DecodeStatsEntry& entry,
AppendDecodeStatsCB append_done_cb) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(IsInitialized());
DVLOG(3) << __func__ << " Reading key " << key.ToLogString()
<< " from DB with intent to update with " << entry.ToLogString();
db_->GetEntry(key.Serialize(),
base::BindOnce(&VideoDecodeStatsDBImpl::WriteUpdatedEntry,
weak_ptr_factory_.GetWeakPtr(), key, entry,
std::move(append_done_cb)));
}
void VideoDecodeStatsDBImpl::GetDecodeStats(const VideoDescKey& key,
GetDecodeStatsCB get_stats_cb) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(IsInitialized());
DVLOG(3) << __func__ << " " << key.ToLogString();
db_->GetEntry(
key.Serialize(),
base::BindOnce(&VideoDecodeStatsDBImpl::OnGotDecodeStats,
weak_ptr_factory_.GetWeakPtr(), std::move(get_stats_cb)));
}
void VideoDecodeStatsDBImpl::WriteUpdatedEntry(
const VideoDescKey& key,
const DecodeStatsEntry& entry,
AppendDecodeStatsCB append_done_cb,
bool read_success,
std::unique_ptr<DecodeStatsProto> prev_stats_proto) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(IsInitialized());
// Note: outcome of "Write" operation logged in OnEntryUpdated().
UMA_HISTOGRAM_BOOLEAN("Media.VideoDecodeStatsDB.OpSuccess.Read",
read_success);
if (!read_success) {
DVLOG(2) << __func__ << " FAILED DB read for " << key.ToLogString()
<< "; ignoring update!";
std::move(append_done_cb).Run(false);
return;
}
if (!prev_stats_proto) {
prev_stats_proto.reset(new DecodeStatsProto());
prev_stats_proto->set_frames_decoded(0);
prev_stats_proto->set_frames_dropped(0);
prev_stats_proto->set_frames_decoded_power_efficient(0);
}
uint64_t sum_frames_decoded =
prev_stats_proto->frames_decoded() + entry.frames_decoded;
uint64_t sum_frames_dropped =
prev_stats_proto->frames_dropped() + entry.frames_dropped;
uint64_t sum_frames_decoded_power_efficient =
prev_stats_proto->frames_decoded_power_efficient() +
entry.frames_decoded_power_efficient;
prev_stats_proto->set_frames_decoded(sum_frames_decoded);
prev_stats_proto->set_frames_dropped(sum_frames_dropped);
prev_stats_proto->set_frames_decoded_power_efficient(
sum_frames_decoded_power_efficient);
DVLOG(3) << __func__ << " Updating " << key.ToLogString() << " with "
<< entry.ToLogString() << " aggregate:"
<< DecodeStatsEntry(sum_frames_decoded, sum_frames_dropped,
sum_frames_decoded_power_efficient)
.ToLogString();
using ProtoDecodeStatsEntry = leveldb_proto::ProtoDatabase<DecodeStatsProto>;
std::unique_ptr<ProtoDecodeStatsEntry::KeyEntryVector> entries =
std::make_unique<ProtoDecodeStatsEntry::KeyEntryVector>();
entries->emplace_back(key.Serialize(), *prev_stats_proto);
db_->UpdateEntries(std::move(entries),
std::make_unique<leveldb_proto::KeyVector>(),
base::BindOnce(&VideoDecodeStatsDBImpl::OnEntryUpdated,
weak_ptr_factory_.GetWeakPtr(),
std::move(append_done_cb)));
}
void VideoDecodeStatsDBImpl::OnEntryUpdated(AppendDecodeStatsCB append_done_cb,
bool success) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
UMA_HISTOGRAM_BOOLEAN("Media.VideoDecodeStatsDB.OpSuccess.Write", success);
DVLOG(3) << __func__ << " update " << (success ? "succeeded" : "FAILED!");
std::move(append_done_cb).Run(success);
}
void VideoDecodeStatsDBImpl::OnGotDecodeStats(
GetDecodeStatsCB get_stats_cb,
bool success,
std::unique_ptr<DecodeStatsProto> stats_proto) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
UMA_HISTOGRAM_BOOLEAN("Media.VideoDecodeStatsDB.OpSuccess.Read", success);
std::unique_ptr<DecodeStatsEntry> entry;
if (stats_proto) {
DCHECK(success);
entry = std::make_unique<DecodeStatsEntry>(
stats_proto->frames_decoded(), stats_proto->frames_dropped(),
stats_proto->frames_decoded_power_efficient());
}
DVLOG(3) << __func__ << " read " << (success ? "succeeded" : "FAILED!")
<< " entry: " << (entry ? entry->ToLogString() : "nullptr");
std::move(get_stats_cb).Run(success, std::move(entry));
}
void VideoDecodeStatsDBImpl::DestroyStats(base::OnceClosure destroy_done_cb) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DVLOG(2) << __func__;
// DB is no longer initialized once destruction kicks off.
db_init_ = false;
db_destroy_pending_ = true;
db_->Destroy(base::BindOnce(&VideoDecodeStatsDBImpl::OnDestroyedStats,
weak_ptr_factory_.GetWeakPtr(),
std::move(destroy_done_cb)));
}
void VideoDecodeStatsDBImpl::OnDestroyedStats(base::OnceClosure destroy_done_cb,
bool success) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DVLOG(2) << __func__ << (success ? " succeeded" : " FAILED!");
UMA_HISTOGRAM_BOOLEAN("Media.VideoDecodeStatsDB.OpSuccess.Destroy", success);
// Allow calls to re-Intialize() now that destruction is complete.
DCHECK(!db_init_);
db_destroy_pending_ = false;
// We don't pass success to |destroy_done_cb|. Clearing is best effort and
// there is no additional action for callers to take in case of failure.
// TODO(chcunningham): Monitor UMA and consider more aggressive action like
// deleting the DB directory.
std::move(destroy_done_cb).Run();
}
} // namespace media