// 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. #include "media/remoting/metrics.h" #include "base/metrics/histogram_functions.h" #include "base/metrics/histogram_macros.h" #include "media/audio/sample_rates.h" namespace media { namespace remoting { namespace { //////////////////////////////////////////////////////////////////////////////// // BEGIN: These were all borrowed from src/media/filters/ffmpeg_demuxer.cc. // TODO(miu): This code will be de-duped in a soon-upcoming change. // Some videos just want to watch the world burn, with a height of 0; cap the // "infinite" aspect ratio resulting. constexpr int kInfiniteRatio = 99999; // Common aspect ratios (multiplied by 100 and truncated) used for histogramming // video sizes. These were taken on 20111103 from // http://wikipedia.org/wiki/Aspect_ratio_(image)#Previous_and_currently_used_aspect_ratios constexpr int kCommonAspectRatios100[] = { 100, 115, 133, 137, 143, 150, 155, 160, 166, 175, 177, 185, 200, 210, 220, 221, 235, 237, 240, 255, 259, 266, 276, 293, 400, 1200, kInfiniteRatio, }; // END: Code borrowed from src/media/filter/ffmpeg_demuxer.cc. //////////////////////////////////////////////////////////////////////////////// // Buckets for video width histograms. constexpr int kVideoWidthBuckets[] = { 180, 240, 320, 480, 640, 720, 872, 940, 1280, 1440, 1600, 1760, 1920, 2560, 3840, 7680, 16384, }; } // namespace SessionMetricsRecorder::SessionMetricsRecorder() : last_audio_codec_(kUnknownAudioCodec), last_channel_layout_(CHANNEL_LAYOUT_NONE), last_sample_rate_(0), last_video_codec_(kUnknownVideoCodec), last_video_profile_(VIDEO_CODEC_PROFILE_UNKNOWN), remote_playback_is_disabled_(false) {} SessionMetricsRecorder::~SessionMetricsRecorder() = default; void SessionMetricsRecorder::WillStartSession(StartTrigger trigger) { DCHECK(!start_trigger_); start_trigger_ = trigger; start_time_ = base::TimeTicks::Now(); } void SessionMetricsRecorder::DidStartSession() { UMA_HISTOGRAM_ENUMERATION("Media.Remoting.SessionStartTrigger", *start_trigger_, START_TRIGGER_MAX + 1); if (last_audio_codec_ != kUnknownAudioCodec) RecordAudioConfiguration(); if (last_video_codec_ != kUnknownVideoCodec) RecordVideoConfiguration(); RecordTrackConfiguration(); } void SessionMetricsRecorder::WillStopSession(StopTrigger trigger) { if (!start_trigger_) return; // Record what triggered the end of the session. UMA_HISTOGRAM_ENUMERATION("Media.Remoting.SessionStopTrigger", trigger, STOP_TRIGGER_MAX + 1); // Record the session duration. const base::TimeDelta session_duration = base::TimeTicks::Now() - start_time_; UMA_HISTOGRAM_CUSTOM_TIMES("Media.Remoting.SessionDuration", session_duration, base::TimeDelta::FromSeconds(15), base::TimeDelta::FromHours(12), 50); // Reset |start_trigger_| since metrics recording of the current remoting // session has now completed. start_trigger_ = base::nullopt; } void SessionMetricsRecorder::OnPipelineMetadataChanged( const PipelineMetadata& metadata) { if (metadata.has_audio && metadata.audio_decoder_config.IsValidConfig()) { const auto& config = metadata.audio_decoder_config; // While in a remoting session, record audio configuration changes. const bool need_to_record_audio_configuration = start_trigger_ && (config.codec() != last_audio_codec_ || config.channel_layout() != last_channel_layout_ || config.samples_per_second() != last_sample_rate_); last_audio_codec_ = config.codec(); last_channel_layout_ = config.channel_layout(); last_sample_rate_ = config.samples_per_second(); if (need_to_record_audio_configuration) RecordAudioConfiguration(); } else { last_audio_codec_ = kUnknownAudioCodec; last_channel_layout_ = CHANNEL_LAYOUT_NONE; last_sample_rate_ = 0; } if (metadata.has_video && metadata.video_decoder_config.IsValidConfig()) { const auto& config = metadata.video_decoder_config; // While in a remoting session, record video configuration changes. const bool need_to_record_video_configuration = start_trigger_ && (config.codec() != last_video_codec_ || config.profile() != last_video_profile_ || metadata.natural_size != last_natural_size_); last_video_codec_ = config.codec(); last_video_profile_ = config.profile(); last_natural_size_ = metadata.natural_size; if (need_to_record_video_configuration) RecordVideoConfiguration(); } else { last_video_codec_ = kUnknownVideoCodec; last_video_profile_ = VIDEO_CODEC_PROFILE_UNKNOWN; last_natural_size_ = gfx::Size(); } // While in a remoting session, record whether audio or video media streams // started or ended. if (start_trigger_) RecordTrackConfiguration(); } void SessionMetricsRecorder::OnRemotePlaybackDisabled(bool disabled) { if (disabled == remote_playback_is_disabled_) return; // De-dupe redundant notifications. UMA_HISTOGRAM_BOOLEAN("Media.Remoting.AllowedByPage", !disabled); remote_playback_is_disabled_ = disabled; } void SessionMetricsRecorder::OnPosterImageDownloaded( base::TimeDelta download_duration, bool success) { const std::string name = success ? "Media.Remoting.PosterDownloadDuration.Success" : "Media.Remoting.PosterDownloadDuration.Fail"; // Note: Not using UMA_HISTOGRAM_CUSTOM_TIMES because |name| is a variable in // in this instance; and so the "one histogram" static local should not be // created. base::UmaHistogramCustomTimes(name, download_duration, base::TimeDelta::FromMilliseconds(10), base::TimeDelta::FromSeconds(30), 50); } void SessionMetricsRecorder::RecordAudioConfiguration() { UMA_HISTOGRAM_ENUMERATION("Media.Remoting.AudioCodec", last_audio_codec_, kAudioCodecMax + 1); UMA_HISTOGRAM_ENUMERATION("Media.Remoting.AudioChannelLayout", last_channel_layout_, CHANNEL_LAYOUT_MAX + 1); AudioSampleRate asr; if (ToAudioSampleRate(last_sample_rate_, &asr)) { UMA_HISTOGRAM_ENUMERATION("Media.Remoting.AudioSamplesPerSecond", asr, kAudioSampleRateMax + 1); } else { UMA_HISTOGRAM_COUNTS_1M("Media.Remoting.AudioSamplesPerSecondUnexpected", last_sample_rate_); } } void SessionMetricsRecorder::RecordVideoConfiguration() { UMA_HISTOGRAM_ENUMERATION("Media.Remoting.VideoCodec", last_video_codec_, kVideoCodecMax + 1); UMA_HISTOGRAM_ENUMERATION("Media.Remoting.VideoCodecProfile", last_video_profile_, VIDEO_CODEC_PROFILE_MAX + 1); UMA_HISTOGRAM_CUSTOM_ENUMERATION( "Media.Remoting.VideoNaturalWidth", last_natural_size_.width(), base::CustomHistogram::ArrayToCustomRanges( kVideoWidthBuckets, arraysize(kVideoWidthBuckets))); // Intentionally use integer division to truncate the result. const int aspect_ratio_100 = last_natural_size_.height() ? (last_natural_size_.width() * 100) / last_natural_size_.height() : kInfiniteRatio; UMA_HISTOGRAM_CUSTOM_ENUMERATION( "Media.Remoting.VideoAspectRatio", aspect_ratio_100, base::CustomHistogram::ArrayToCustomRanges( kCommonAspectRatios100, arraysize(kCommonAspectRatios100))); } void SessionMetricsRecorder::RecordTrackConfiguration() { TrackConfiguration config = NEITHER_AUDIO_NOR_VIDEO; if (last_audio_codec_ != kUnknownAudioCodec) config = AUDIO_ONLY; if (last_video_codec_ != kUnknownVideoCodec) { if (config == AUDIO_ONLY) config = AUDIO_AND_VIDEO; else config = VIDEO_ONLY; } UMA_HISTOGRAM_ENUMERATION("Media.Remoting.TrackConfiguration", config, TRACK_CONFIGURATION_MAX + 1); } RendererMetricsRecorder::RendererMetricsRecorder() : start_time_(base::TimeTicks::Now()), did_record_first_playout_(false) {} RendererMetricsRecorder::~RendererMetricsRecorder() = default; void RendererMetricsRecorder::OnRendererInitialized() { const base::TimeDelta elapsed_since_start = base::TimeTicks::Now() - start_time_; UMA_HISTOGRAM_CUSTOM_TIMES("Media.Remoting.TimeUntilRemoteInitialized", elapsed_since_start, base::TimeDelta::FromMilliseconds(10), base::TimeDelta::FromSeconds(30), 50); } void RendererMetricsRecorder::OnEvidenceOfPlayoutAtReceiver() { if (did_record_first_playout_) return; const base::TimeDelta elapsed_since_start = base::TimeTicks::Now() - start_time_; UMA_HISTOGRAM_CUSTOM_TIMES("Media.Remoting.TimeUntilFirstPlayout", elapsed_since_start, base::TimeDelta::FromMilliseconds(10), base::TimeDelta::FromSeconds(30), 50); did_record_first_playout_ = true; } void RendererMetricsRecorder::OnAudioRateEstimate(int kilobits_per_second) { UMA_HISTOGRAM_CUSTOM_COUNTS("Media.Remoting.AudioBitrate", kilobits_per_second, 1, 1024, 50); } void RendererMetricsRecorder::OnVideoRateEstimate(int kilobits_per_second) { UMA_HISTOGRAM_CUSTOM_COUNTS("Media.Remoting.VideoBitrate", kilobits_per_second, 1, 16 * 1024, 50); } } // namespace remoting } // namespace media