[M120 Migration][MM][CAPI] Fix the logic for media using capi player.
[platform/framework/web/chromium-efl.git] / media / mojo / services / video_decode_perf_history.cc
1 // Copyright 2017 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "media/mojo/services/video_decode_perf_history.h"
6
7 #include "base/format_macros.h"
8 #include "base/functional/bind.h"
9 #include "base/functional/callback.h"
10 #include "base/logging.h"
11 #include "base/memory/ptr_util.h"
12 #include "base/metrics/field_trial_params.h"
13 #include "base/strings/stringprintf.h"
14 #include "base/task/bind_post_task.h"
15 #include "base/task/single_thread_task_runner.h"
16 #include "media/base/key_systems.h"
17 #include "media/base/media_switches.h"
18 #include "media/base/video_codecs.h"
19 #include "media/capabilities/learning_helper.h"
20 #include "media/mojo/mojom/media_types.mojom.h"
21 #include "services/metrics/public/cpp/ukm_builders.h"
22 #include "services/metrics/public/cpp/ukm_recorder.h"
23
24 namespace media {
25
26 namespace {
27
28 const double kMaxSmoothDroppedFramesPercentParamDefault = .05;
29
30 }  // namespace
31
32 const char VideoDecodePerfHistory::kMaxSmoothDroppedFramesPercentParamName[] =
33     "smooth_threshold";
34
35 const char
36     VideoDecodePerfHistory::kEmeMaxSmoothDroppedFramesPercentParamName[] =
37         "eme_smooth_threshold";
38
39 // static
40 double VideoDecodePerfHistory::GetMaxSmoothDroppedFramesPercent(bool is_eme) {
41   double threshold = base::GetFieldTrialParamByFeatureAsDouble(
42       kMediaCapabilitiesWithParameters, kMaxSmoothDroppedFramesPercentParamName,
43       kMaxSmoothDroppedFramesPercentParamDefault);
44
45   // For EME, the precedence of overrides is:
46   // 1. EME specific override, |k*Eme*MaxSmoothDroppedFramesPercentParamName
47   // 2. Non-EME override, |kMaxSmoothDroppedFramesPercentParamName|
48   // 3. |kMaxSmoothDroppedFramesPercentParamDefault|
49   if (is_eme) {
50     threshold = base::GetFieldTrialParamByFeatureAsDouble(
51         kMediaCapabilitiesWithParameters,
52         kEmeMaxSmoothDroppedFramesPercentParamName, threshold);
53   }
54
55   return threshold;
56 }
57
58 // static
59 base::FieldTrialParams VideoDecodePerfHistory::GetFieldTrialParams() {
60   base::FieldTrialParams actual_trial_params;
61
62   const bool result = base::GetFieldTrialParamsByFeature(
63       kMediaCapabilitiesWithParameters, &actual_trial_params);
64   DCHECK(result);
65
66   return actual_trial_params;
67 }
68
69 VideoDecodePerfHistory::VideoDecodePerfHistory(
70     std::unique_ptr<VideoDecodeStatsDB> db,
71     learning::FeatureProviderFactoryCB feature_factory_cb)
72     : db_(std::move(db)),
73       db_init_status_(UNINITIALIZED),
74       feature_factory_cb_(std::move(feature_factory_cb)) {
75   DVLOG(2) << __func__;
76   DCHECK(db_);
77
78   // If the local learning experiment is enabled, then also create
79   // |learning_helper_| to send data to it.
80   if (base::FeatureList::IsEnabled(kMediaLearningExperiment))
81     learning_helper_ = std::make_unique<LearningHelper>(feature_factory_cb_);
82 }
83
84 VideoDecodePerfHistory::~VideoDecodePerfHistory() {
85   DVLOG(2) << __func__;
86   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
87 }
88
89 void VideoDecodePerfHistory::BindReceiver(
90     mojo::PendingReceiver<mojom::VideoDecodePerfHistory> receiver) {
91   DVLOG(3) << __func__;
92   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
93   receivers_.Add(this, std::move(receiver));
94 }
95
96 void VideoDecodePerfHistory::InitDatabase() {
97   DVLOG(2) << __func__;
98   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
99
100   if (db_init_status_ == PENDING)
101     return;
102
103   // DB should be initialized only once! We hand out references to the
104   // initialized DB via GetVideoDecodeStatsDB(). Dependents expect DB to remain
105   // initialized during their lifetime.
106   DCHECK_EQ(db_init_status_, UNINITIALIZED);
107
108   db_->Initialize(base::BindOnce(&VideoDecodePerfHistory::OnDatabaseInit,
109                                  weak_ptr_factory_.GetWeakPtr()));
110   db_init_status_ = PENDING;
111 }
112
113 void VideoDecodePerfHistory::OnDatabaseInit(bool success) {
114   DVLOG(2) << __func__ << " " << success;
115   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
116   DCHECK_EQ(db_init_status_, PENDING);
117
118   db_init_status_ = success ? COMPLETE : FAILED;
119
120   // Post all the deferred API calls as if they're just now coming in.
121   for (auto& deferred_call : init_deferred_api_calls_) {
122     base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
123         FROM_HERE, std::move(deferred_call));
124   }
125   init_deferred_api_calls_.clear();
126 }
127
128 void VideoDecodePerfHistory::GetPerfInfo(mojom::PredictionFeaturesPtr features,
129                                          GetPerfInfoCallback got_info_cb) {
130   DVLOG(3) << __func__;
131   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
132
133   DCHECK_NE(features->profile, VIDEO_CODEC_PROFILE_UNKNOWN);
134   DCHECK_GT(features->frames_per_sec, 0);
135   DCHECK(features->video_size.width() > 0 && features->video_size.height() > 0);
136
137   if (db_init_status_ == FAILED) {
138     // Optimistically claim perf is both smooth and power efficient.
139     std::move(got_info_cb).Run(true, true);
140     return;
141   }
142
143   // Defer this request until the DB is initialized.
144   if (db_init_status_ != COMPLETE) {
145     init_deferred_api_calls_.push_back(base::BindOnce(
146         &VideoDecodePerfHistory::GetPerfInfo, weak_ptr_factory_.GetWeakPtr(),
147         std::move(features), std::move(got_info_cb)));
148     InitDatabase();
149     return;
150   }
151
152   VideoDecodeStatsDB::VideoDescKey video_key =
153       VideoDecodeStatsDB::VideoDescKey::MakeBucketedKey(
154           features->profile, features->video_size, features->frames_per_sec,
155           features->key_system, features->use_hw_secure_codecs);
156
157   db_->GetDecodeStats(
158       video_key, base::BindOnce(&VideoDecodePerfHistory::OnGotStatsForRequest,
159                                 weak_ptr_factory_.GetWeakPtr(), video_key,
160                                 std::move(got_info_cb)));
161 }
162
163 void VideoDecodePerfHistory::AssessStats(
164     const VideoDecodeStatsDB::VideoDescKey& key,
165     const VideoDecodeStatsDB::DecodeStatsEntry* stats,
166     bool* is_smooth,
167     bool* is_power_efficient) {
168   // TODO(chcunningham/mlamouri): Refactor database API to give us nearby
169   // stats whenever we don't have a perfect match. If higher
170   // resolutions/frame rates are known to be smooth, we can report this as
171   /// smooth. If lower resolutions/frames are known to be janky, we can assume
172   // this will be janky.
173
174   // No stats? Lets be optimistic.
175   if (!stats || stats->frames_decoded == 0) {
176     *is_power_efficient = true;
177     *is_smooth = true;
178     return;
179   }
180
181   double percent_dropped =
182       static_cast<double>(stats->frames_dropped) / stats->frames_decoded;
183   double percent_power_efficient =
184       static_cast<double>(stats->frames_power_efficient) /
185       stats->frames_decoded;
186
187   *is_power_efficient =
188       percent_power_efficient >= kMinPowerEfficientDecodedFramePercent;
189
190   *is_smooth = percent_dropped <=
191                GetMaxSmoothDroppedFramesPercent(!key.key_system.empty());
192 }
193
194 void VideoDecodePerfHistory::OnGotStatsForRequest(
195     const VideoDecodeStatsDB::VideoDescKey& video_key,
196     GetPerfInfoCallback got_info_cb,
197     bool database_success,
198     std::unique_ptr<VideoDecodeStatsDB::DecodeStatsEntry> stats) {
199   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
200   DCHECK(got_info_cb);
201   DCHECK_EQ(db_init_status_, COMPLETE);
202
203   bool is_power_efficient = false;
204   bool is_smooth = false;
205   double percent_dropped = 0;
206   double percent_power_efficient = 0;
207
208   AssessStats(video_key, stats.get(), &is_smooth, &is_power_efficient);
209
210   if (stats && stats->frames_decoded) {
211     DCHECK(database_success);
212     percent_dropped =
213         static_cast<double>(stats->frames_dropped) / stats->frames_decoded;
214     percent_power_efficient =
215         static_cast<double>(stats->frames_power_efficient) /
216         stats->frames_decoded;
217   }
218
219   DVLOG(3) << __func__
220            << base::StringPrintf(
221                   " profile:%s size:%s fps:%d --> ",
222                   GetProfileName(video_key.codec_profile).c_str(),
223                   video_key.size.ToString().c_str(), video_key.frame_rate)
224            << (stats.get()
225                    ? base::StringPrintf(
226                          "smooth:%d frames_decoded:%" PRIu64 " pcnt_dropped:%f"
227                          " pcnt_power_efficent:%f",
228                          is_smooth, stats->frames_decoded, percent_dropped,
229                          percent_power_efficient)
230                    : (database_success ? "no info" : "query FAILED"));
231
232   std::move(got_info_cb).Run(is_smooth, is_power_efficient);
233 }
234
235 VideoDecodePerfHistory::SaveCallback VideoDecodePerfHistory::GetSaveCallback() {
236   return base::BindRepeating(&VideoDecodePerfHistory::SavePerfRecord,
237                              weak_ptr_factory_.GetWeakPtr());
238 }
239
240 void VideoDecodePerfHistory::SavePerfRecord(ukm::SourceId source_id,
241                                             learning::FeatureValue origin,
242                                             bool is_top_frame,
243                                             mojom::PredictionFeatures features,
244                                             mojom::PredictionTargets targets,
245                                             uint64_t player_id,
246                                             base::OnceClosure save_done_cb) {
247   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
248   DVLOG(3)
249       << __func__
250       << base::StringPrintf(
251              " profile:%s size:%s fps:%f decoded:%d dropped:%d efficient:%d",
252              GetProfileName(features.profile).c_str(),
253              features.video_size.ToString().c_str(), features.frames_per_sec,
254              targets.frames_decoded, targets.frames_dropped,
255              targets.frames_power_efficient);
256
257   if (db_init_status_ == FAILED) {
258     DVLOG(3) << __func__ << " Can't save stats. No DB!";
259     return;
260   }
261
262   // Defer this request until the DB is initialized.
263   if (db_init_status_ != COMPLETE) {
264     init_deferred_api_calls_.push_back(base::BindOnce(
265         &VideoDecodePerfHistory::SavePerfRecord, weak_ptr_factory_.GetWeakPtr(),
266         source_id, origin, is_top_frame, std::move(features),
267         std::move(targets), player_id, std::move(save_done_cb)));
268     InitDatabase();
269     return;
270   }
271
272   VideoDecodeStatsDB::VideoDescKey video_key =
273       VideoDecodeStatsDB::VideoDescKey::MakeBucketedKey(
274           features.profile, features.video_size, features.frames_per_sec,
275           features.key_system, features.use_hw_secure_codecs);
276   VideoDecodeStatsDB::DecodeStatsEntry new_stats(
277       targets.frames_decoded, targets.frames_dropped,
278       targets.frames_power_efficient);
279
280   if (learning_helper_)
281     learning_helper_->AppendStats(video_key, origin, new_stats);
282
283   // Get past perf info and report UKM metrics before saving this record.
284   db_->GetDecodeStats(
285       video_key,
286       base::BindOnce(&VideoDecodePerfHistory::OnGotStatsForSave,
287                      weak_ptr_factory_.GetWeakPtr(), source_id, is_top_frame,
288                      player_id, video_key, new_stats, std::move(save_done_cb)));
289 }
290
291 void VideoDecodePerfHistory::OnGotStatsForSave(
292     ukm::SourceId source_id,
293     bool is_top_frame,
294     uint64_t player_id,
295     const VideoDecodeStatsDB::VideoDescKey& video_key,
296     const VideoDecodeStatsDB::DecodeStatsEntry& new_stats,
297     base::OnceClosure save_done_cb,
298     bool success,
299     std::unique_ptr<VideoDecodeStatsDB::DecodeStatsEntry> past_stats) {
300   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
301   DCHECK_EQ(db_init_status_, COMPLETE);
302
303   if (!success) {
304     DVLOG(3) << __func__ << " FAILED! Aborting save.";
305     if (save_done_cb)
306       std::move(save_done_cb).Run();
307     return;
308   }
309
310   ReportUkmMetrics(source_id, is_top_frame, player_id, video_key, new_stats,
311                    past_stats.get());
312
313   db_->AppendDecodeStats(
314       video_key, new_stats,
315       base::BindOnce(&VideoDecodePerfHistory::OnSaveDone,
316                      weak_ptr_factory_.GetWeakPtr(), std::move(save_done_cb)));
317 }
318
319 void VideoDecodePerfHistory::OnSaveDone(base::OnceClosure save_done_cb,
320                                         bool success) {
321   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
322
323   // TODO(chcunningham): Monitor UMA. Experiment with re-initializing DB to
324   // remedy IO failures.
325   DVLOG(3) << __func__ << (success ? " succeeded" : " FAILED!");
326
327   // Don't bother to bubble success. Its not actionable for upper layers. Also,
328   // save_done_cb only used for test sequencing, where DB should always behave
329   // (or fail the test).
330   if (save_done_cb)
331     std::move(save_done_cb).Run();
332 }
333
334 void VideoDecodePerfHistory::ReportUkmMetrics(
335     ukm::SourceId source_id,
336     bool is_top_frame,
337     uint64_t player_id,
338     const VideoDecodeStatsDB::VideoDescKey& video_key,
339     const VideoDecodeStatsDB::DecodeStatsEntry& new_stats,
340     VideoDecodeStatsDB::DecodeStatsEntry* past_stats) {
341   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
342
343   // UKM may be unavailable in content_shell or other non-chrome/ builds; it
344   // may also be unavailable if browser shutdown has started; so this may be a
345   // nullptr. If it's unavailable, UKM reporting will be skipped.
346   ukm::UkmRecorder* ukm_recorder = ukm::UkmRecorder::Get();
347   if (!ukm_recorder)
348     return;
349
350   ukm::builders::Media_VideoDecodePerfRecord builder(source_id);
351   builder.SetVideo_InTopFrame(is_top_frame);
352   builder.SetVideo_PlayerID(player_id);
353
354   builder.SetVideo_CodecProfile(video_key.codec_profile);
355   builder.SetVideo_FramesPerSecond(video_key.frame_rate);
356   builder.SetVideo_NaturalHeight(video_key.size.height());
357   builder.SetVideo_NaturalWidth(video_key.size.width());
358
359   if (!video_key.key_system.empty()) {
360     builder.SetVideo_EME_KeySystem(GetKeySystemIntForUKM(video_key.key_system));
361     builder.SetVideo_EME_UseHwSecureCodecs(video_key.use_hw_secure_codecs);
362   }
363
364   bool past_is_smooth = false;
365   bool past_is_efficient = false;
366   AssessStats(video_key, past_stats, &past_is_smooth, &past_is_efficient);
367   builder.SetPerf_ApiWouldClaimIsSmooth(past_is_smooth);
368   builder.SetPerf_ApiWouldClaimIsPowerEfficient(past_is_efficient);
369   if (past_stats) {
370     builder.SetPerf_PastVideoFramesDecoded(past_stats->frames_decoded);
371     builder.SetPerf_PastVideoFramesDropped(past_stats->frames_dropped);
372     builder.SetPerf_PastVideoFramesPowerEfficient(
373         past_stats->frames_power_efficient);
374   } else {
375     builder.SetPerf_PastVideoFramesDecoded(0);
376     builder.SetPerf_PastVideoFramesDropped(0);
377     builder.SetPerf_PastVideoFramesPowerEfficient(0);
378   }
379
380   bool new_is_smooth = false;
381   bool new_is_efficient = false;
382   AssessStats(video_key, &new_stats, &new_is_smooth, &new_is_efficient);
383   builder.SetPerf_RecordIsSmooth(new_is_smooth);
384   builder.SetPerf_RecordIsPowerEfficient(new_is_efficient);
385   builder.SetPerf_VideoFramesDecoded(new_stats.frames_decoded);
386   builder.SetPerf_VideoFramesDropped(new_stats.frames_dropped);
387   builder.SetPerf_VideoFramesPowerEfficient(new_stats.frames_power_efficient);
388
389   builder.Record(ukm_recorder);
390 }
391
392 void VideoDecodePerfHistory::ClearHistory(base::OnceClosure clear_done_cb) {
393   DVLOG(2) << __func__;
394   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
395
396   // If we have a learning helper, then replace it.  This will erase any data
397   // that it currently has.
398   if (learning_helper_)
399     learning_helper_ = std::make_unique<LearningHelper>(feature_factory_cb_);
400
401   if (db_init_status_ == FAILED) {
402     DVLOG(3) << __func__ << " Can't clear history - No DB!";
403     std::move(clear_done_cb).Run();
404     return;
405   }
406
407   // Defer this request until the DB is initialized.
408   if (db_init_status_ != COMPLETE) {
409     init_deferred_api_calls_.push_back(base::BindOnce(
410         &VideoDecodePerfHistory::ClearHistory, weak_ptr_factory_.GetWeakPtr(),
411         std::move(clear_done_cb)));
412     InitDatabase();
413     return;
414   }
415
416   db_->ClearStats(base::BindOnce(&VideoDecodePerfHistory::OnClearedHistory,
417                                  weak_ptr_factory_.GetWeakPtr(),
418                                  std::move(clear_done_cb)));
419 }
420
421 void VideoDecodePerfHistory::OnClearedHistory(base::OnceClosure clear_done_cb) {
422   DVLOG(2) << __func__;
423   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
424
425   std::move(clear_done_cb).Run();
426 }
427
428 void VideoDecodePerfHistory::GetVideoDecodeStatsDB(GetCB get_db_cb) {
429   DVLOG(3) << __func__;
430   DCHECK(get_db_cb);
431   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
432
433   if (db_init_status_ == FAILED) {
434     std::move(get_db_cb).Run(nullptr);
435     return;
436   }
437
438   // Defer this request until the DB is initialized.
439   if (db_init_status_ != COMPLETE) {
440     init_deferred_api_calls_.push_back(
441         base::BindOnce(&VideoDecodePerfHistory::GetVideoDecodeStatsDB,
442                        weak_ptr_factory_.GetWeakPtr(), std::move(get_db_cb)));
443     InitDatabase();
444     return;
445   }
446
447   // DB is already initialized. base::BindPostTaskToCurrentDefault to avoid
448   // reentrancy.
449   std::move(base::BindPostTaskToCurrentDefault(std::move(get_db_cb)))
450       .Run(db_.get());
451 }
452
453 }  // namespace media