blob: c82ab5a445ff3d63e6d95c6b2ff2f058ea725640 [file] [log] [blame]
/*
* Copyright (c) 2021 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#include "video/frame_cadence_adapter.h"
#include <atomic>
#include <memory>
#include <utility>
#include "api/sequence_checker.h"
#include "api/task_queue/task_queue_base.h"
#include "rtc_base/logging.h"
#include "rtc_base/race_checker.h"
#include "rtc_base/synchronization/mutex.h"
#include "rtc_base/task_utils/pending_task_safety_flag.h"
#include "rtc_base/task_utils/to_queued_task.h"
#include "system_wrappers/include/clock.h"
#include "system_wrappers/include/field_trial.h"
#include "system_wrappers/include/metrics.h"
namespace webrtc {
namespace {
class FrameCadenceAdapterImpl : public FrameCadenceAdapterInterface {
public:
FrameCadenceAdapterImpl(Clock* clock, TaskQueueBase* queue);
// FrameCadenceAdapterInterface overrides.
void Initialize(Callback* callback) override;
void SetZeroHertzModeEnabled(bool enabled) override;
// VideoFrameSink overrides.
void OnFrame(const VideoFrame& frame) override;
void OnDiscardedFrame() override { callback_->OnDiscardedFrame(); }
void OnConstraintsChanged(
const VideoTrackSourceConstraints& constraints) override;
private:
// Called from OnFrame in zero-hertz mode.
void OnFrameOnMainQueue(Timestamp post_time,
int frames_scheduled_for_processing,
const VideoFrame& frame) RTC_RUN_ON(queue_);
// Called to report on constraint UMAs.
void MaybeReportFrameRateConstraintUmas() RTC_RUN_ON(&queue_);
Clock* const clock_;
TaskQueueBase* const queue_;
// True if we support frame entry for screenshare with a minimum frequency of
// 0 Hz.
const bool zero_hertz_screenshare_enabled_;
// Set up during Initialize.
Callback* callback_ = nullptr;
// The source's constraints.
absl::optional<VideoTrackSourceConstraints> source_constraints_
RTC_GUARDED_BY(queue_);
// Whether zero-hertz and UMA reporting is enabled.
bool zero_hertz_and_uma_reporting_enabled_ RTC_GUARDED_BY(queue_) = false;
// Race checker for incoming frames. This is the network thread in chromium,
// but may vary from test contexts.
rtc::RaceChecker incoming_frame_race_checker_;
bool has_reported_screenshare_frame_rate_umas_ RTC_GUARDED_BY(queue_) = false;
// Number of frames that are currently scheduled for processing on the
// |queue_|.
std::atomic<int> frames_scheduled_for_processing_{0};
ScopedTaskSafetyDetached safety_;
};
FrameCadenceAdapterImpl::FrameCadenceAdapterImpl(Clock* clock,
TaskQueueBase* queue)
: clock_(clock),
queue_(queue),
zero_hertz_screenshare_enabled_(
field_trial::IsEnabled("WebRTC-ZeroHertzScreenshare")) {}
void FrameCadenceAdapterImpl::Initialize(Callback* callback) {
callback_ = callback;
}
void FrameCadenceAdapterImpl::SetZeroHertzModeEnabled(bool enabled) {
RTC_DCHECK_RUN_ON(queue_);
if (enabled && !zero_hertz_and_uma_reporting_enabled_)
has_reported_screenshare_frame_rate_umas_ = false;
zero_hertz_and_uma_reporting_enabled_ = enabled;
}
void FrameCadenceAdapterImpl::OnFrame(const VideoFrame& frame) {
// This method is called on the network thread under Chromium, or other
// various contexts in test.
RTC_DCHECK_RUNS_SERIALIZED(&incoming_frame_race_checker_);
// Local time in webrtc time base.
Timestamp post_time = clock_->CurrentTime();
frames_scheduled_for_processing_.fetch_add(1, std::memory_order_relaxed);
queue_->PostTask(ToQueuedTask(safety_.flag(), [this, post_time, frame] {
RTC_DCHECK_RUN_ON(queue_);
const int frames_scheduled_for_processing =
frames_scheduled_for_processing_.fetch_sub(1,
std::memory_order_relaxed);
OnFrameOnMainQueue(post_time, frames_scheduled_for_processing,
std::move(frame));
MaybeReportFrameRateConstraintUmas();
}));
}
void FrameCadenceAdapterImpl::OnConstraintsChanged(
const VideoTrackSourceConstraints& constraints) {
RTC_LOG(LS_INFO) << __func__ << " min_fps "
<< constraints.min_fps.value_or(-1) << " max_fps "
<< constraints.max_fps.value_or(-1);
queue_->PostTask(ToQueuedTask(safety_.flag(), [this, constraints] {
RTC_DCHECK_RUN_ON(queue_);
source_constraints_ = constraints;
}));
}
// RTC_RUN_ON(queue_)
void FrameCadenceAdapterImpl::OnFrameOnMainQueue(
Timestamp post_time,
int frames_scheduled_for_processing,
const VideoFrame& frame) {
callback_->OnFrame(post_time, frames_scheduled_for_processing, frame);
}
// RTC_RUN_ON(queue_)
void FrameCadenceAdapterImpl::MaybeReportFrameRateConstraintUmas() {
if (has_reported_screenshare_frame_rate_umas_)
return;
has_reported_screenshare_frame_rate_umas_ = true;
if (!zero_hertz_and_uma_reporting_enabled_)
return;
RTC_HISTOGRAM_BOOLEAN("WebRTC.Screenshare.FrameRateConstraints.Exists",
source_constraints_.has_value());
if (!source_constraints_.has_value())
return;
RTC_HISTOGRAM_BOOLEAN("WebRTC.Screenshare.FrameRateConstraints.Min.Exists",
source_constraints_->min_fps.has_value());
if (source_constraints_->min_fps.has_value()) {
RTC_HISTOGRAM_COUNTS_100(
"WebRTC.Screenshare.FrameRateConstraints.Min.Value",
source_constraints_->min_fps.value());
}
RTC_HISTOGRAM_BOOLEAN("WebRTC.Screenshare.FrameRateConstraints.Max.Exists",
source_constraints_->max_fps.has_value());
if (source_constraints_->max_fps.has_value()) {
RTC_HISTOGRAM_COUNTS_100(
"WebRTC.Screenshare.FrameRateConstraints.Max.Value",
source_constraints_->max_fps.value());
}
if (!source_constraints_->min_fps.has_value()) {
if (source_constraints_->max_fps.has_value()) {
RTC_HISTOGRAM_COUNTS_100(
"WebRTC.Screenshare.FrameRateConstraints.MinUnset.Max",
source_constraints_->max_fps.value());
}
} else if (source_constraints_->max_fps.has_value()) {
if (source_constraints_->min_fps.value() <
source_constraints_->max_fps.value()) {
RTC_HISTOGRAM_COUNTS_100(
"WebRTC.Screenshare.FrameRateConstraints.MinLessThanMax.Min",
source_constraints_->min_fps.value());
RTC_HISTOGRAM_COUNTS_100(
"WebRTC.Screenshare.FrameRateConstraints.MinLessThanMax.Max",
source_constraints_->max_fps.value());
}
// Multi-dimensional histogram for min and max FPS making it possible to
// uncover min and max combinations. See
// https://chromium.googlesource.com/chromium/src.git/+/HEAD/tools/metrics/histograms/README.md#multidimensional-histograms
constexpr int kMaxBucketCount =
60 * /*max min_fps=*/60 + /*max max_fps=*/60 - 1;
RTC_HISTOGRAM_ENUMERATION_SPARSE(
"WebRTC.Screenshare.FrameRateConstraints.60MinPlusMaxMinusOne",
source_constraints_->min_fps.value() * 60 +
source_constraints_->max_fps.value() - 1,
/*boundary=*/kMaxBucketCount);
}
}
} // namespace
std::unique_ptr<FrameCadenceAdapterInterface>
FrameCadenceAdapterInterface::Create(Clock* clock, TaskQueueBase* queue) {
return std::make_unique<FrameCadenceAdapterImpl>(clock, queue);
}
} // namespace webrtc