blob: ac9b2e1ebf0ae1ca86f16f16824d28fc239393f0 [file] [log] [blame]
// Copyright 2022 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.
#ifndef MEDIA_CAPABILITIES_WEBRTC_VIDEO_STATS_DB_IMPL_H_
#define MEDIA_CAPABILITIES_WEBRTC_VIDEO_STATS_DB_IMPL_H_
#include <memory>
#include "base/cancelable_callback.h"
#include "base/containers/flat_map.h"
#include "base/files/file_path.h"
#include "base/memory/weak_ptr.h"
#include "base/metrics/field_trial_params.h"
#include "base/time/time.h"
#include "components/leveldb_proto/public/proto_database.h"
#include "media/base/media_export.h"
#include "media/base/video_codecs.h"
#include "media/capabilities/webrtc_video_stats_db.h"
#include "ui/gfx/geometry/size.h"
namespace base {
class FilePath;
class Clock;
} // namespace base
namespace leveldb_proto {
class ProtoDatabaseProvider;
} // namespace leveldb_proto
namespace media {
class WebrtcVideoStatsEntryProto;
// LevelDB implementation of WebrtcVideoStatsDB. This class is not
// thread safe. All API calls should happen on the same sequence used for
// construction. API callbacks will also occur on this sequence.
class MEDIA_EXPORT WebrtcVideoStatsDBImpl : public WebrtcVideoStatsDB {
public:
// Create an instance! `db_dir` specifies where to store LevelDB files to
// disk. LevelDB generates a handful of files, so its recommended to provide a
// dedicated directory to keep them isolated.
static std::unique_ptr<WebrtcVideoStatsDBImpl> Create(
base::FilePath db_dir,
leveldb_proto::ProtoDatabaseProvider* db_provider);
~WebrtcVideoStatsDBImpl() override;
WebrtcVideoStatsDBImpl(const WebrtcVideoStatsDBImpl&) = delete;
WebrtcVideoStatsDBImpl& operator=(const WebrtcVideoStatsDBImpl&) = delete;
// Implement WebrtcVideoStatsDB.
void Initialize(InitializeCB init_cb) override;
void AppendVideoStats(const VideoDescKey& key,
const VideoStats& video_stats,
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:
friend class WebrtcVideoStatsDBImplTest;
using PendingOpId = int;
// Private constructor only called by tests (friends). Production code
// should always use the static Create() method.
explicit WebrtcVideoStatsDBImpl(
std::unique_ptr<leveldb_proto::ProtoDatabase<WebrtcVideoStatsEntryProto>>
db);
// Creates a PendingOperation using `uma_str` and adds it to `pending_ops_`
// map. Returns PendingOpId for newly started operation. Callers must later
// call CompletePendingOp() with this id to destroy the PendingOperation and
// finalize timing UMA.
PendingOpId StartPendingOp(std::string uma_str);
// Removes PendingOperation from `pending_ops_` using `op_id_` as a key. This
// destroys the object and triggers timing UMA.
void CompletePendingOp(PendingOpId op_id);
// Unified handler for timeouts of pending DB operations. PendingOperation
// will be notified that it timed out (to trigger timing UMA) and removed from
// `pending_ops_`.
void OnPendingOpTimeout(PendingOpId id);
// Helper to report timing information for DB operations, including when they
// hang indefinitely.
class PendingOperation {
public:
PendingOperation(
std::string uma_str,
std::unique_ptr<base::CancelableOnceClosure> timeout_closure);
// Records task timing UMA if it hasn't already timed out.
virtual ~PendingOperation();
// Copies disallowed. Incompatible with move-only members and UMA logging in
// the destructor.
PendingOperation(const PendingOperation&) = delete;
PendingOperation& operator=(const PendingOperation&) = delete;
// Trigger UMA recording for timeout.
void OnTimeout();
private:
friend class WebrtcVideoStatsDBImplTest;
std::string uma_str_;
std::unique_ptr<base::CancelableOnceClosure> timeout_closure_;
base::TimeTicks start_ticks_;
};
// Map of operation id -> outstanding PendingOperations.
base::flat_map<PendingOpId, std::unique_ptr<PendingOperation>> pending_ops_;
// Called when the database has been initialized. Will immediately call
// `init_cb` to forward `success`.
void OnInit(PendingOpId id,
InitializeCB init_cb,
leveldb_proto::Enums::InitStatus status);
// Returns true if the DB is successfully initialized.
bool IsInitialized();
// Passed as the callback for `OnGotVideoStats` by `AppendVideoStats` to
// update the database once we've read the existing stats entry.
void WriteUpdatedEntry(
PendingOpId op_id,
const VideoDescKey& key,
const VideoStats& new_video_stats,
AppendVideoStatsCB append_done_cb,
bool read_success,
std::unique_ptr<WebrtcVideoStatsEntryProto> stats_proto);
// Called when the database has been modified after a call to
// `WriteUpdatedEntry`. Will run `append_done_cb` when done.
void OnEntryUpdated(PendingOpId op_id,
AppendVideoStatsCB append_done_cb,
bool success);
// Called when GetVideoStats() operation was performed. `get_stats_cb`
// will be run with `success` and a `VideoStatsEntry` created from
// `stats_proto` or nullptr if no entry was found for the requested key.
void OnGotVideoStats(PendingOpId op_id,
GetVideoStatsCB get_stats_cb,
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,
base::OnceClosure clear_done_cb,
bool success);
// Validates the stats entry. If true is returned the stats are sorted in the
// correct order and contain values that are somewhat reasonable.
bool AreStatsValid(const WebrtcVideoStatsEntryProto* const stats_proto);
void set_wall_clock_for_test(const base::Clock* tick_clock) {
wall_clock_ = tick_clock;
}
// Next PendingOpId for use in `pending_ops_` map. See StartPendingOp().
PendingOpId next_op_id_ = 0;
// Indicates whether initialization is completed. Does not indicate whether it
// was successful. Will be reset upon calling DestroyStats(). Failed
// initialization is signaled by setting `db_` to null.
bool db_init_ = false;
// ProtoDatabase instance. Set to nullptr if fatal database error is
// encountered. Each entry in the DB is expected to be around 200 bytes. It is
// expected that there will be at most ~100 entries so the total database size
// is expected to not exceed 20 kB.
std::unique_ptr<leveldb_proto::ProtoDatabase<WebrtcVideoStatsEntryProto>> db_;
// For getting wall-clock time. Tests may override via
// set_wall_clock_for_test().
const base::Clock* wall_clock_ = nullptr;
// Ensures all access to class members come on the same sequence. API calls
// and callbacks should occur on the same sequence used during construction.
// LevelDB operations happen on a separate task runner, but all LevelDB
// callbacks to this happen on the checked sequence.
SEQUENCE_CHECKER(sequence_checker_);
base::WeakPtrFactory<WebrtcVideoStatsDBImpl> weak_ptr_factory_{this};
};
} // namespace media
#endif // MEDIA_CAPABILITIES_WEBRTC_VIDEO_STATS_DB_IMPL_H_