summaryrefslogtreecommitdiff
path: root/chromium/media/capabilities/video_decode_stats_db_impl.h
blob: fb041b04475699df5c118ea37f98a340693a1bb6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
// 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.

#ifndef MEDIA_CAPABILITIES_VIDEO_DECODE_STATS_DB_IMPL_H_
#define MEDIA_CAPABILITIES_VIDEO_DECODE_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 "components/leveldb_proto/public/proto_database.h"
#include "media/base/media_export.h"
#include "media/base/video_codecs.h"
#include "media/capabilities/video_decode_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 DecodeStatsProto;

// LevelDB implementation of VideoDecodeStatsDB. 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 VideoDecodeStatsDBImpl : public VideoDecodeStatsDB {
 public:
  static const char kMaxFramesPerBufferParamName[];
  static const char kMaxDaysToKeepStatsParamName[];
  static const char kEnableUnweightedEntriesParamName[];

  // 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<VideoDecodeStatsDBImpl> Create(
      base::FilePath db_dir,
      leveldb_proto::ProtoDatabaseProvider* db_provider);

  ~VideoDecodeStatsDBImpl() override;

  // Implement VideoDecodeStatsDB.
  void Initialize(InitializeCB init_cb) override;
  void AppendDecodeStats(const VideoDescKey& key,
                         const DecodeStatsEntry& entry,
                         AppendDecodeStatsCB append_done_cb) override;
  void GetDecodeStats(const VideoDescKey& key,
                      GetDecodeStatsCB get_stats_cb) override;
  void ClearStats(base::OnceClosure clear_done_cb) override;

 private:
  friend class VideoDecodeStatsDBImplTest;

  using PendingOpId = int;

  // Private constructor only called by tests (friends). Production code
  // should always use the static Create() method.
  VideoDecodeStatsDBImpl(
      std::unique_ptr<leveldb_proto::ProtoDatabase<DecodeStatsProto>> db);

  // Default |last_write_time| for DB entries that lack a time stamp due to
  // using an earlier version of DecodeStatsProto. Date chosen so old stats from
  // previous version will expire (unless new stats arrive) roughly 2 months
  // after the proto update hits the chrome Stable channel (M71).
  static constexpr char kDefaultWriteTime[] = "01-FEB-2019 12:00pm";

  // Number of decoded frames to keep in the rolling "window" for a given entry
  // in the database.
  static int GetMaxFramesPerBuffer();

  // Number of days after which stats will be discarded if not updated. This
  // avoids users getting stuck with a bad capability prediction that may have
  // been due to one-off circumstances.
  static int GetMaxDaysToKeepStats();

  // When true, each playback entry in the DB should be given equal weight
  // regardless of how many frames were decoded.
  static bool GetEnableUnweightedEntries();

  // Returns current feature params.
  static base::FieldTrialParams GetFieldTrialParams();

  // 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
  // |penidng_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 VideoDecodeStatsDBImplTest;

    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 |OnGotDecodeStats| by |AppendDecodeStats| to
  // update the database once we've read the existing stats entry.
  void WriteUpdatedEntry(PendingOpId op_id,
                         const VideoDescKey& key,
                         const DecodeStatsEntry& entry,
                         AppendDecodeStatsCB append_done_cb,
                         bool read_success,
                         std::unique_ptr<DecodeStatsProto> 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,
                      AppendDecodeStatsCB append_done_cb,
                      bool success);

  // Called when GetDecodeStats() operation was performed. |get_stats_cb|
  // will be run with |success| and a |DecodeStatsEntry| created from
  // |stats_proto| or nullptr if no entry was found for the requested key.
  void OnGotDecodeStats(PendingOpId op_id,
                        GetDecodeStatsCB get_stats_cb,
                        bool success,
                        std::unique_ptr<DecodeStatsProto> 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);

  // Return true if:
  //    values aren't corrupted nonsense (e.g. way more frames dropped than
  //    decoded, or number of frames_decoded < frames_power_efficient)
  // &&
  //    stats aren't expired.
  //       ("now" - stats_proto.last_write_date > GeMaxDaysToKeepStats())
  bool AreStatsUsable(const DecodeStatsProto* 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.
  std::unique_ptr<leveldb_proto::ProtoDatabase<DecodeStatsProto>> db_;

  // For getting wall-clock time. Tests may override via SetClockForTest().
  const base::Clock* wall_clock_ = nullptr;

  // Stores parsed value of |kDefaultWriteTime|.
  base::Time default_write_time_;

  // 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<VideoDecodeStatsDBImpl> weak_ptr_factory_{this};

  DISALLOW_COPY_AND_ASSIGN(VideoDecodeStatsDBImpl);
};

}  // namespace media

#endif  // MEDIA_CAPABILITIES_VIDEO_DECODE_STATS_DB_IMPL_H_