Introduce VideoDecodeStatsReporter for Media Capabilities

Each WebMediaPlayer will create a VideoDecodeStatsReporter to send
smoothness (and eventually power) statistics across to the browser-side
VideoDecodeStatsRecorder.

The statistics will be "finalized" upon
- player destruction
- significant media property changes (e.g. resolution, codec, fps)
- tab close

Follow up CLs will implement the browser-side recording of the stats
and add a UMA to "grade" the media capabilites API by comparing the
observed stats against the API's claims of smoothness.

Bug: 695264
Cq-Include-Trybots: master.tryserver.chromium.android:android_optional_gpu_tests_rel;master.tryserver.chromium.linux:linux_optional_gpu_tests_rel;master.tryserver.chromium.linux:linux_site_isolation;master.tryserver.chromium.mac:mac_optional_gpu_tests_rel;master.tryserver.chromium.win:win_optional_gpu_tests_rel
Change-Id: I78ca453fa936c9a48813fbc92dc67714f9956f63
Reviewed-on: https://chromium-review.googlesource.com/570920
Commit-Queue: Chrome Cunningham <[email protected]>
Reviewed-by: Daniel Cheng <[email protected]>
Reviewed-by: Dale Curtis <[email protected]>
Reviewed-by: Charlie Reis <[email protected]>
Cr-Commit-Position: refs/heads/master@{#497948}
diff --git a/media/blink/webmediaplayer_impl.cc b/media/blink/webmediaplayer_impl.cc
index 8bdd7ee..637c89d 100644
--- a/media/blink/webmediaplayer_impl.cc
+++ b/media/blink/webmediaplayer_impl.cc
@@ -41,6 +41,7 @@
 #include "media/base/timestamp_constants.h"
 #include "media/base/video_frame.h"
 #include "media/blink/texttrack_impl.h"
+#include "media/blink/video_decode_stats_reporter.h"
 #include "media/blink/watch_time_reporter.h"
 #include "media/blink/webaudiosourceprovider_impl.h"
 #include "media/blink/webcontentdecryptionmodule_impl.h"
@@ -254,7 +255,9 @@
           base::FeatureList::IsEnabled(media::kUseSurfaceLayerForVideo)),
       request_routing_token_cb_(params->request_routing_token_cb()),
       overlay_routing_token_(OverlayInfo::RoutingToken()),
-      watch_time_recorder_provider_(params->watch_time_recorder_provider()) {
+      watch_time_recorder_provider_(params->watch_time_recorder_provider()),
+      create_decode_stats_recorder_cb_(
+          params->create_capabilities_recorder_cb()) {
   DVLOG(1) << __func__;
   DCHECK(!adjust_allocated_memory_cb_.is_null());
   DCHECK(renderer_factory_selector_);
@@ -550,6 +553,9 @@
     watch_time_reporter_->OnPlaying();
   }
 
+  if (video_decode_stats_reporter_)
+    video_decode_stats_reporter_->OnPlaying();
+
   media_log_->AddEvent(media_log_->CreateEvent(MediaLogEvent::PLAY));
   UpdatePlayState();
 }
@@ -585,6 +591,10 @@
 
   DCHECK(watch_time_reporter_);
   watch_time_reporter_->OnPaused();
+
+  if (video_decode_stats_reporter_)
+    video_decode_stats_reporter_->OnPaused();
+
   media_log_->AddEvent(media_log_->CreateEvent(MediaLogEvent::PAUSE));
 
   UpdatePlayState();
@@ -1103,6 +1113,9 @@
   if (!was_encrypted && watch_time_reporter_)
     CreateWatchTimeReporter();
 
+  // For now MediaCapabilities only handles clear content.
+  video_decode_stats_reporter_.reset();
+
   SetCdm(cdm);
 }
 
@@ -1120,6 +1133,9 @@
   if (!was_encrypted && watch_time_reporter_)
     CreateWatchTimeReporter();
 
+  // For now MediaCapabilities only handles clear content.
+  video_decode_stats_reporter_.reset();
+
   encrypted_client_->Encrypted(
       ConvertToWebInitDataType(init_data_type), init_data.data(),
       base::saturated_cast<unsigned int>(init_data.size()));
@@ -1415,9 +1431,44 @@
     observer_->OnMetadataChanged(pipeline_metadata_);
 
   CreateWatchTimeReporter();
+  CreateVideoDecodeStatsReporter();
   UpdatePlayState();
 }
 
+void WebMediaPlayerImpl::CreateVideoDecodeStatsReporter() {
+  // TODO(chcunningham): destroy reporter if we initially have video but the
+  // track gets disabled. Currently not possible in default desktop Chrome.
+  if (!HasVideo())
+    return;
+
+  // Stats reporter requires a valid config. We may not have one for HLS cases
+  // where URL demuxer doesn't know details of the stream.
+  if (!pipeline_metadata_.video_decoder_config.IsValidConfig())
+    return;
+
+  // For now MediaCapabilities only handles clear content.
+  // TODO(chcunningham): Report encrypted stats.
+  if (is_encrypted_)
+    return;
+
+  // Create capabilities reporter and synchronize its initial state.
+  video_decode_stats_reporter_.reset(new VideoDecodeStatsReporter(
+      create_decode_stats_recorder_cb_.Run(),
+      base::Bind(&WebMediaPlayerImpl::GetPipelineStatistics,
+                 base::Unretained(this)),
+      pipeline_metadata_.video_decoder_config));
+
+  if (delegate_->IsFrameHidden())
+    video_decode_stats_reporter_->OnHidden();
+  else
+    video_decode_stats_reporter_->OnShown();
+
+  if (paused_)
+    video_decode_stats_reporter_->OnPaused();
+  else
+    video_decode_stats_reporter_->OnPlaying();
+}
+
 void WebMediaPlayerImpl::OnProgress() {
   DVLOG(4) << __func__;
   if (highest_ready_state_ < ReadyState::kReadyStateHaveFutureData) {
@@ -1576,6 +1627,9 @@
   if (!watch_time_reporter_->IsSizeLargeEnoughToReportWatchTime())
     CreateWatchTimeReporter();
 
+  if (video_decode_stats_reporter_)
+    video_decode_stats_reporter_->OnNaturalSizeChanged(rotated_size);
+
   if (overlay_enabled_ && surface_manager_ &&
       overlay_mode_ == OverlayMode::kUseContentVideoView) {
     surface_manager_->NaturalSizeChanged(rotated_size);
@@ -1624,6 +1678,9 @@
 
   if (observer_)
     observer_->OnMetadataChanged(pipeline_metadata_);
+
+  if (video_decode_stats_reporter_)
+    video_decode_stats_reporter_->OnVideoConfigChanged(config);
 }
 
 void WebMediaPlayerImpl::OnVideoAverageKeyframeDistanceUpdate() {
@@ -1643,6 +1700,9 @@
   if (watch_time_reporter_)
     watch_time_reporter_->OnHidden();
 
+  if (video_decode_stats_reporter_)
+    video_decode_stats_reporter_->OnHidden();
+
   UpdateBackgroundVideoOptimizationState();
   UpdatePlayState();
 
@@ -1674,6 +1734,9 @@
   if (watch_time_reporter_)
     watch_time_reporter_->OnShown();
 
+  if (video_decode_stats_reporter_)
+    video_decode_stats_reporter_->OnShown();
+
   // Only track the time to the first frame if playing or about to play because
   // of being shown and only for videos we would optimize background playback
   // for.
@@ -1778,6 +1841,9 @@
 void WebMediaPlayerImpl::OnDisconnectedFromRemoteDevice(double t) {
   DoSeek(base::TimeDelta::FromSecondsD(t), false);
 
+  // Capabilities reporting can resume now that playback is local.
+  CreateVideoDecodeStatsReporter();
+
   // |client_| might destroy us in methods below.
   UpdatePlayState();
 
@@ -1787,6 +1853,9 @@
 }
 
 void WebMediaPlayerImpl::SuspendForRemote() {
+  // Capabilities reporting should only be performed for local playbacks.
+  video_decode_stats_reporter_.reset();
+
   if (pipeline_controller_.IsPipelineSuspended() &&
       !IsNewRemotePlaybackPipelineEnabled()) {
     scoped_refptr<VideoFrame> frame = cast_impl_.GetCastingBanner();
@@ -1993,6 +2062,10 @@
     // TODO(tguilbert/avayvod): Update this flag when removing |cast_impl_|.
     using_media_player_renderer_ = true;
 
+    // MediaPlayerRenderer does not provide pipeline stats, so nuke capabilities
+    // reporter.
+    video_decode_stats_reporter_.reset();
+
     demuxer_.reset(new MediaUrlDemuxer(media_task_runner_, loaded_url_,
                                        frame_->GetDocument().SiteForCookies()));
     pipeline_controller_.Start(demuxer_.get(), this, false, false);
@@ -2670,6 +2743,10 @@
     const std::string& remote_device_friendly_name) {
   DCHECK(main_task_runner_->BelongsToCurrentThread());
   disable_pipeline_auto_suspend_ = true;
+
+  // Capabilities reporting should only be performed for local playbacks.
+  video_decode_stats_reporter_.reset();
+
   // Requests to restart media pipeline. A remote renderer will be created via
   // the |renderer_factory_selector_|.
   ScheduleRestart();
@@ -2682,6 +2759,10 @@
 void WebMediaPlayerImpl::SwitchToLocalRenderer() {
   DCHECK(main_task_runner_->BelongsToCurrentThread());
   disable_pipeline_auto_suspend_ = false;
+
+  // Capabilities reporting may resume now that playback is local.
+  CreateVideoDecodeStatsReporter();
+
   // Requests to restart media pipeline. A local renderer will be created via
   // the |renderer_factory_selector_|.
   ScheduleRestart();