| Index: media/blink/watch_time_reporter.cc
|
| diff --git a/media/blink/watch_time_reporter.cc b/media/blink/watch_time_reporter.cc
|
| index 06e97646739b1c5b4db09cca46756447e74a3394..f279e9c238cf3abe07986f225a4fb343b8ef0d90 100644
|
| --- a/media/blink/watch_time_reporter.cc
|
| +++ b/media/blink/watch_time_reporter.cc
|
| @@ -30,6 +30,25 @@ WatchTimeReporter::WatchTimeReporter(bool has_audio,
|
| scoped_refptr<MediaLog> media_log,
|
| const gfx::Size& initial_video_size,
|
| const GetMediaTimeCB& get_media_time_cb)
|
| + : WatchTimeReporter(has_audio,
|
| + has_video,
|
| + is_mse,
|
| + is_encrypted,
|
| + is_embedded_media_experience_enabled,
|
| + std::move(media_log),
|
| + initial_video_size,
|
| + get_media_time_cb,
|
| + false) {}
|
| +
|
| +WatchTimeReporter::WatchTimeReporter(bool has_audio,
|
| + bool has_video,
|
| + bool is_mse,
|
| + bool is_encrypted,
|
| + bool is_embedded_media_experience_enabled,
|
| + scoped_refptr<MediaLog> media_log,
|
| + const gfx::Size& initial_video_size,
|
| + const GetMediaTimeCB& get_media_time_cb,
|
| + bool is_background)
|
| : has_audio_(has_audio),
|
| has_video_(has_video),
|
| is_mse_(is_mse),
|
| @@ -38,15 +57,34 @@ WatchTimeReporter::WatchTimeReporter(bool has_audio,
|
| is_embedded_media_experience_enabled),
|
| media_log_(std::move(media_log)),
|
| initial_video_size_(initial_video_size),
|
| - get_media_time_cb_(get_media_time_cb) {
|
| + get_media_time_cb_(get_media_time_cb),
|
| + is_background_(is_background) {
|
| DCHECK(!get_media_time_cb_.is_null());
|
| DCHECK(has_audio_ || has_video_);
|
|
|
| if (base::PowerMonitor* pm = base::PowerMonitor::Get())
|
| pm->AddObserver(this);
|
| +
|
| + if (is_background_) {
|
| + DCHECK(has_audio_);
|
| + DCHECK(!has_video_);
|
| + return;
|
| + }
|
| +
|
| + // Background watch time is reported by creating an audio only watch time
|
| + // reporter which receives play when hidden and pause when shown. This avoids
|
| + // unnecessary complexity inside the UpdateWatchTime() for handling this case.
|
| + if (has_video_ && has_audio_ && ShouldReportWatchTime()) {
|
| + background_reporter_.reset(
|
| + new WatchTimeReporter(has_audio_, false, is_mse_, is_encrypted_,
|
| + is_embedded_media_experience_enabled_, media_log_,
|
| + initial_video_size_, get_media_time_cb_, true));
|
| + }
|
| }
|
|
|
| WatchTimeReporter::~WatchTimeReporter() {
|
| + background_reporter_.reset();
|
| +
|
| // If the timer is still running, finalize immediately, this is our last
|
| // chance to capture metrics.
|
| if (reporting_timer_.IsRunning())
|
| @@ -57,16 +95,25 @@ WatchTimeReporter::~WatchTimeReporter() {
|
| }
|
|
|
| void WatchTimeReporter::OnPlaying() {
|
| + if (background_reporter_ && !is_visible_)
|
| + background_reporter_->OnPlaying();
|
| +
|
| is_playing_ = true;
|
| MaybeStartReportingTimer(get_media_time_cb_.Run());
|
| }
|
|
|
| void WatchTimeReporter::OnPaused() {
|
| + if (background_reporter_)
|
| + background_reporter_->OnPaused();
|
| +
|
| is_playing_ = false;
|
| MaybeFinalizeWatchTime(FinalizeTime::ON_NEXT_UPDATE);
|
| }
|
|
|
| void WatchTimeReporter::OnSeeking() {
|
| + if (background_reporter_)
|
| + background_reporter_->OnSeeking();
|
| +
|
| if (!reporting_timer_.IsRunning())
|
| return;
|
|
|
| @@ -80,6 +127,9 @@ void WatchTimeReporter::OnSeeking() {
|
| }
|
|
|
| void WatchTimeReporter::OnVolumeChange(double volume) {
|
| + if (background_reporter_)
|
| + background_reporter_->OnVolumeChange(volume);
|
| +
|
| const double old_volume = volume_;
|
| volume_ = volume;
|
|
|
| @@ -91,6 +141,9 @@ void WatchTimeReporter::OnVolumeChange(double volume) {
|
| }
|
|
|
| void WatchTimeReporter::OnShown() {
|
| + if (background_reporter_)
|
| + background_reporter_->OnPaused();
|
| +
|
| if (!has_video_)
|
| return;
|
|
|
| @@ -99,6 +152,9 @@ void WatchTimeReporter::OnShown() {
|
| }
|
|
|
| void WatchTimeReporter::OnHidden() {
|
| + if (background_reporter_ && is_playing_)
|
| + background_reporter_->OnPlaying();
|
| +
|
| if (!has_video_)
|
| return;
|
|
|
| @@ -157,7 +213,8 @@ void WatchTimeReporter::MaybeStartReportingTimer(
|
| if (reporting_timer_.IsRunning())
|
| return;
|
|
|
| - last_media_timestamp_ = end_timestamp_for_power_ = kNoTimestamp;
|
| + last_media_timestamp_ = last_media_power_timestamp_ =
|
| + end_timestamp_for_power_ = kNoTimestamp;
|
| is_on_battery_power_ = IsOnBatteryPower();
|
| start_timestamp_ = start_timestamp_for_power_ = start_timestamp;
|
| reporting_timer_.Start(FROM_HERE, reporting_interval_, this,
|
| @@ -197,78 +254,73 @@ void WatchTimeReporter::UpdateWatchTime() {
|
| is_finalizing ? end_timestamp_ : get_media_time_cb_.Run();
|
| const base::TimeDelta elapsed = current_timestamp - start_timestamp_;
|
|
|
| + std::unique_ptr<MediaLogEvent> log_event =
|
| + media_log_->CreateEvent(MediaLogEvent::Type::WATCH_TIME_UPDATE);
|
| +
|
| +#define RECORD_WATCH_TIME(key, value) \
|
| + do { \
|
| + log_event->params.SetDoubleWithoutPathExpansion( \
|
| + has_video_ \
|
| + ? MediaLog::kWatchTimeAudioVideo##key \
|
| + : (is_background_ ? MediaLog::kWatchTimeAudioVideoBackground##key \
|
| + : MediaLog::kWatchTimeAudio##key), \
|
| + value.InSecondsF()); \
|
| + } while (0)
|
| +
|
| // Only report watch time after some minimum amount has elapsed. Don't update
|
| // watch time if media time hasn't changed since the last run; this may occur
|
| // if a seek is taking some time to complete or the playback is stalled for
|
| // some reason.
|
| - if (elapsed >= kMinimumElapsedWatchTime &&
|
| - last_media_timestamp_ != current_timestamp) {
|
| + if (last_media_timestamp_ != current_timestamp) {
|
| last_media_timestamp_ = current_timestamp;
|
|
|
| - std::unique_ptr<MediaLogEvent> log_event =
|
| - media_log_->CreateEvent(MediaLogEvent::Type::WATCH_TIME_UPDATE);
|
| -
|
| - log_event->params.SetDoubleWithoutPathExpansion(
|
| - has_video_ ? MediaLog::kWatchTimeAudioVideoAll
|
| - : MediaLog::kWatchTimeAudioAll,
|
| - elapsed.InSecondsF());
|
| - if (is_mse_) {
|
| - log_event->params.SetDoubleWithoutPathExpansion(
|
| - has_video_ ? MediaLog::kWatchTimeAudioVideoMse
|
| - : MediaLog::kWatchTimeAudioMse,
|
| - elapsed.InSecondsF());
|
| - } else {
|
| - log_event->params.SetDoubleWithoutPathExpansion(
|
| - has_video_ ? MediaLog::kWatchTimeAudioVideoSrc
|
| - : MediaLog::kWatchTimeAudioSrc,
|
| - elapsed.InSecondsF());
|
| - }
|
| - if (is_encrypted_) {
|
| - log_event->params.SetDoubleWithoutPathExpansion(
|
| - has_video_ ? MediaLog::kWatchTimeAudioVideoEme
|
| - : MediaLog::kWatchTimeAudioEme,
|
| - elapsed.InSecondsF());
|
| - }
|
| + if (elapsed >= kMinimumElapsedWatchTime) {
|
| + RECORD_WATCH_TIME(All, elapsed);
|
| + if (is_mse_)
|
| + RECORD_WATCH_TIME(Mse, elapsed);
|
| + else
|
| + RECORD_WATCH_TIME(Src, elapsed);
|
|
|
| - if (is_embedded_media_experience_enabled_) {
|
| - log_event->params.SetDoubleWithoutPathExpansion(
|
| - has_video_ ? MediaLog::kWatchTimeAudioVideoEmbeddedExperience
|
| - : MediaLog::kWatchTimeAudioEmbeddedExperience,
|
| - elapsed.InSecondsF());
|
| + if (is_encrypted_)
|
| + RECORD_WATCH_TIME(Eme, elapsed);
|
| +
|
| + if (is_embedded_media_experience_enabled_)
|
| + RECORD_WATCH_TIME(EmbeddedExperience, elapsed);
|
| }
|
| + }
|
| +
|
| + if (last_media_power_timestamp_ != current_timestamp) {
|
| + // We need a separate |last_media_power_timestamp_| since we don't always
|
| + // base the last watch time calculation on the current timestamp.
|
| + last_media_power_timestamp_ =
|
| + is_power_change_pending ? end_timestamp_for_power_ : current_timestamp;
|
|
|
| // Record watch time using the last known value for |is_on_battery_power_|;
|
| // if there's a |pending_power_change_| use that to accurately finalize the
|
| // last bits of time in the previous bucket.
|
| const base::TimeDelta elapsed_power =
|
| - (is_power_change_pending ? end_timestamp_for_power_
|
| - : current_timestamp) -
|
| - start_timestamp_for_power_;
|
| + last_media_power_timestamp_ - start_timestamp_for_power_;
|
|
|
| // Again, only update watch time if enough time has elapsed; we need to
|
| // recheck the elapsed time here since the power source can change anytime.
|
| if (elapsed_power >= kMinimumElapsedWatchTime) {
|
| - if (is_on_battery_power_) {
|
| - log_event->params.SetDoubleWithoutPathExpansion(
|
| - has_video_ ? MediaLog::kWatchTimeAudioVideoBattery
|
| - : MediaLog::kWatchTimeAudioBattery,
|
| - elapsed_power.InSecondsF());
|
| - } else {
|
| - log_event->params.SetDoubleWithoutPathExpansion(
|
| - has_video_ ? MediaLog::kWatchTimeAudioVideoAc
|
| - : MediaLog::kWatchTimeAudioAc,
|
| - elapsed_power.InSecondsF());
|
| - }
|
| + if (is_on_battery_power_)
|
| + RECORD_WATCH_TIME(Battery, elapsed_power);
|
| + else
|
| + RECORD_WATCH_TIME(Ac, elapsed_power);
|
| }
|
| + }
|
| +#undef RECORD_WATCH_TIME
|
|
|
| - if (is_finalizing)
|
| - log_event->params.SetBoolean(MediaLog::kWatchTimeFinalize, true);
|
| - else if (is_power_change_pending)
|
| - log_event->params.SetBoolean(MediaLog::kWatchTimeFinalizePower, true);
|
| + // Always send finalize, even if we don't currently have any data, it's
|
| + // harmless to send since nothing will be logged if we've already finalized.
|
| + if (is_finalizing)
|
| + log_event->params.SetBoolean(MediaLog::kWatchTimeFinalize, true);
|
| + else if (is_power_change_pending)
|
| + log_event->params.SetBoolean(MediaLog::kWatchTimeFinalizePower, true);
|
|
|
| - DVLOG(2) << "Sending watch time update.";
|
| + if (!log_event->params.empty())
|
| media_log_->AddEvent(std::move(log_event));
|
| - }
|
|
|
| if (is_power_change_pending) {
|
| // Invert battery power status here instead of using the value returned by
|
|
|