blob: ab1e425ac40a6341ae2f94e0f2d4f7e36dc3cfaf [file] [log] [blame]
// Copyright 2022 The Cobalt Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "cobalt/dom/source_buffer_metrics.h"
#include <algorithm>
#include "base/logging.h"
#include "base/metrics/histogram_macros.h"
#include "base/time/time.h"
#include "cobalt/base/statistics.h"
#include "starboard/common/string.h"
#include "starboard/common/time.h"
#include "starboard/once.h"
#include "starboard/types.h"
namespace cobalt {
namespace dom {
#if !defined(COBALT_BUILD_TYPE_GOLD)
namespace {
int GetBandwidth(std::size_t size, int64_t duration_us) {
return duration_us == 0
? 0
: size * base::Time::kMicrosecondsPerSecond / duration_us;
}
int64_t GetBandwidthForStatistics(int64_t size, int64_t duration) {
return GetBandwidth(static_cast<std::size_t>(size), duration);
}
using BandwidthStatistics =
base::Statistics<int64_t, int64_t, 1024, GetBandwidthForStatistics>;
class StatisticsWrapper {
public:
static StatisticsWrapper* GetInstance();
BandwidthStatistics accumulated_wall_time_bandwidth_{
"DOM.Performance.BandwidthStatsWallTime"};
BandwidthStatistics accumulated_thread_time_bandwidth_{
"DOM.Performance.BandwidthStatsThreadTime"};
};
double GetWallToThreadTimeRatio(int64_t wall_time, int64_t thread_time) {
if (thread_time == 0) {
thread_time = 1;
}
return static_cast<double>(wall_time) / thread_time;
}
} // namespace
SB_ONCE_INITIALIZE_FUNCTION(StatisticsWrapper, StatisticsWrapper::GetInstance);
#endif // !defined(COBALT_BUILD_TYPE_GOLD)
void SourceBufferMetrics::StartTracking(SourceBufferMetricsAction action) {
if (!is_primary_video_) {
return;
}
DCHECK(!is_tracking_);
DCHECK(current_action_ == SourceBufferMetricsAction::NO_ACTION);
DCHECK(action != SourceBufferMetricsAction::NO_ACTION);
is_tracking_ = true;
current_action_ = action;
wall_start_time_ = clock_->NowTicks();
#if !defined(COBALT_BUILD_TYPE_GOLD)
thread_start_time_ = starboard::CurrentMonotonicThreadTime();
#endif // !defined(COBALT_BUILD_TYPE_GOLD)
}
void SourceBufferMetrics::EndTracking(SourceBufferMetricsAction action,
std::size_t size_appended) {
if (!is_primary_video_) {
return;
}
DCHECK(is_tracking_);
is_tracking_ = false;
DCHECK(action == current_action_);
current_action_ = SourceBufferMetricsAction::NO_ACTION;
base::TimeDelta wall_duration = clock_->NowTicks() - wall_start_time_;
RecordTelemetry(action, wall_duration);
#if !defined(COBALT_BUILD_TYPE_GOLD)
int64_t thread_duration =
starboard::CurrentMonotonicThreadTime() - thread_start_time_;
total_wall_time_ += wall_duration.InMicroseconds();
total_thread_time_ += thread_duration;
total_size_ += size_appended;
if (size_appended > 0) {
int wall_bandwidth =
GetBandwidth(size_appended, wall_duration.InMicroseconds());
int thread_bandwidth = GetBandwidth(size_appended, thread_duration);
max_wall_bandwidth_ = std::max(max_wall_bandwidth_, wall_bandwidth);
min_wall_bandwidth_ = std::min(min_wall_bandwidth_, wall_bandwidth);
max_thread_bandwidth_ = std::max(max_thread_bandwidth_, thread_bandwidth);
min_thread_bandwidth_ = std::min(min_thread_bandwidth_, thread_bandwidth);
}
#endif // !defined(COBALT_BUILD_TYPE_GOLD)
}
void SourceBufferMetrics::RecordTelemetry(
SourceBufferMetricsAction action, const base::TimeDelta& action_duration) {
switch (action) {
case SourceBufferMetricsAction::PREPARE_APPEND:
UMA_HISTOGRAM_CUSTOM_MICROSECONDS_TIMES(
"Cobalt.Media.SourceBuffer.PrepareAppend.Timing", action_duration,
base::TimeDelta::FromMicroseconds(1),
base::TimeDelta::FromMilliseconds(25), 50);
break;
case SourceBufferMetricsAction::APPEND_BUFFER:
UMA_HISTOGRAM_CUSTOM_MICROSECONDS_TIMES(
"Cobalt.Media.SourceBuffer.AppendBuffer.Timing", action_duration,
base::TimeDelta::FromMicroseconds(1),
base::TimeDelta::FromMilliseconds(25), 50);
break;
default:
UMA_HISTOGRAM_CUSTOM_MICROSECONDS_TIMES(
"Cobalt.Media.SourceBuffer.Other.Timing", action_duration,
base::TimeDelta::FromMicroseconds(1),
base::TimeDelta::FromMilliseconds(25), 50);
break;
}
}
#if !defined(COBALT_BUILD_TYPE_GOLD)
void SourceBufferMetrics::PrintCurrentMetricsAndUpdateAccumulatedMetrics() {
if (!is_primary_video_) {
return;
}
StatisticsWrapper::GetInstance()->accumulated_wall_time_bandwidth_.AddSample(
total_size_, total_wall_time_);
StatisticsWrapper::GetInstance()
->accumulated_thread_time_bandwidth_.AddSample(total_size_,
total_thread_time_);
LOG_IF(INFO, total_thread_time_ > total_wall_time_)
<< "Total thread time " << total_thread_time_
<< " should not be greater than total wall time " << total_wall_time_
<< ".";
// clang-format off
LOG(INFO) << starboard::FormatString(
"AppendBuffer() metrics:\n"
" Total size of appended data: %zu B, wall time / thread time = %02g\n"
" Total append wall time: %" PRId64 " us (%d B/s)\n"
" Max wall bandwidth: %d B/s\n"
" Min wall bandwidth: %d B/s\n"
" Total append thread time: %" PRId64 " us (%d B/s)\n"
" Max thread bandwidth: %d B/s\n"
" Min thread bandwidth: %d B/s\n",
total_size_,
GetWallToThreadTimeRatio(total_wall_time_, total_thread_time_),
total_wall_time_, GetBandwidth(total_size_, total_wall_time_),
max_wall_bandwidth_, min_wall_bandwidth_, total_thread_time_,
GetBandwidth(total_size_, total_thread_time_), max_thread_bandwidth_,
min_thread_bandwidth_);
// clang-format on
}
void SourceBufferMetrics::PrintAccumulatedMetrics() {
if (!is_primary_video_) {
return;
}
LOG(INFO) << starboard::FormatString(
"Accumulated AppendBuffer() metrics:\n"
" wall time / thread time = %02g\n"
" wall bandwidth statistics (B/s):\n"
" min %d, median %d, average %d, max %d\n"
" thread bandwidth statistics (B/s):\n"
" min %d, median %d, average %d, max %d",
GetWallToThreadTimeRatio(
StatisticsWrapper::GetInstance()
->accumulated_wall_time_bandwidth_.accumulated_divisor(),
StatisticsWrapper::GetInstance()
->accumulated_thread_time_bandwidth_.accumulated_divisor()),
static_cast<int>(StatisticsWrapper::GetInstance()
->accumulated_wall_time_bandwidth_.min()),
static_cast<int>(StatisticsWrapper::GetInstance()
->accumulated_wall_time_bandwidth_.GetMedian()),
static_cast<int>(StatisticsWrapper::GetInstance()
->accumulated_wall_time_bandwidth_.average()),
static_cast<int>(StatisticsWrapper::GetInstance()
->accumulated_wall_time_bandwidth_.max()),
static_cast<int>(StatisticsWrapper::GetInstance()
->accumulated_thread_time_bandwidth_.min()),
static_cast<int>(StatisticsWrapper::GetInstance()
->accumulated_thread_time_bandwidth_.GetMedian()),
static_cast<int>(StatisticsWrapper::GetInstance()
->accumulated_thread_time_bandwidth_.average()),
static_cast<int>(StatisticsWrapper::GetInstance()
->accumulated_thread_time_bandwidth_.max()));
}
#endif // !defined(COBALT_BUILD_TYPE_GOLD)
} // namespace dom
} // namespace cobalt