From 8039cdbe48f8c8bb91fa1761f807005a7b497196 Mon Sep 17 00:00:00 2001 From: Markus Handell Date: Wed, 1 Nov 2023 14:24:25 +0100 Subject: [PATCH] Measure wall clock time of capture and encode processing. (NOTE: This and dependent CLs will be reverted in a few days after data collection from the field is complete.) This change introduces a new task queue concept, Voucher. They are associated with a currently running task tree. Whenever tasks are posted, the current voucher is inherited and set as current in the new task. The voucher exists for as long as there are direct and indirect tasks running that descend from the task where the voucher was created. Vouchers aggregate application-specific attachments, which perform logic unrelated to Voucher progression. This particular change adds an attachment that measures time from capture to all encode operations complete, and places it into the WebRTC.Video.CaptureToSendTimeMs UMA. An accompanying Chrome change crrev.com/c/4992282 ensures survival of vouchers across certain Mojo IPC. Bug: chromium:1498378 Change-Id: I2a27800a4e5504f219d8b9d33c56a48904cf6dde Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/325400 Reviewed-by: Harald Alvestrand Commit-Queue: Markus Handell Cr-Commit-Position: refs/heads/main@{#41061} --- api/task_queue/BUILD.gn | 1 + api/task_queue/task_queue_base.cc | 18 +++++++ api/task_queue/task_queue_base.h | 9 ++-- rtc_base/BUILD.gn | 15 ++++++ rtc_base/voucher.cc | 80 +++++++++++++++++++++++++++++++ rtc_base/voucher.h | 78 ++++++++++++++++++++++++++++++ video/BUILD.gn | 1 + video/video_stream_encoder.cc | 34 +++++++++++++ 8 files changed, 233 insertions(+), 3 deletions(-) create mode 100644 rtc_base/voucher.cc create mode 100644 rtc_base/voucher.h diff --git a/api/task_queue/BUILD.gn b/api/task_queue/BUILD.gn index e0e2e50514..a410c53a9b 100644 --- a/api/task_queue/BUILD.gn +++ b/api/task_queue/BUILD.gn @@ -20,6 +20,7 @@ rtc_library("task_queue") { "..:location", "../../rtc_base:checks", "../../rtc_base:macromagic", + "../../rtc_base:voucher", "../../rtc_base/system:rtc_export", "../units:time_delta", ] diff --git a/api/task_queue/task_queue_base.cc b/api/task_queue/task_queue_base.cc index ecdc7f7691..78c9a271c5 100644 --- a/api/task_queue/task_queue_base.cc +++ b/api/task_queue/task_queue_base.cc @@ -14,6 +14,7 @@ #include "absl/functional/any_invocable.h" #include "api/units/time_delta.h" #include "rtc_base/checks.h" +#include "rtc_base/voucher.h" #if defined(ABSL_HAVE_THREAD_LOCAL) @@ -28,6 +29,23 @@ TaskQueueBase* TaskQueueBase::Current() { return current; } +void TaskQueueBase::PostTask(absl::AnyInvocable task, + const Location& location) { + PostTaskInternal(std::move(task), PostTaskTraits{}, location); +} + +void TaskQueueBase::PostTaskInternal(absl::AnyInvocable task, + const PostTaskTraits& traits, + const Location& location) { + auto current = Voucher::Current(); + PostTaskImpl( + [task = std::move(task), current = std::move(current)]() mutable { + Voucher::ScopedSetter setter(std::move(current)); + std::move(task)(); + }, + traits, location); +} + TaskQueueBase::CurrentTaskQueueSetter::CurrentTaskQueueSetter( TaskQueueBase* task_queue) : previous_(current) { diff --git a/api/task_queue/task_queue_base.h b/api/task_queue/task_queue_base.h index 89e9e9e3b0..b2857163f1 100644 --- a/api/task_queue/task_queue_base.h +++ b/api/task_queue/task_queue_base.h @@ -64,9 +64,7 @@ class RTC_LOCKABLE RTC_EXPORT TaskQueueBase { // // May be called on any thread or task queue, including this task queue. void PostTask(absl::AnyInvocable task, - const Location& location = Location::Current()) { - PostTaskImpl(std::move(task), PostTaskTraits{}, location); - } + const Location& location = Location::Current()); // Prefer PostDelayedTask() over PostDelayedHighPrecisionTask() whenever // possible. @@ -187,6 +185,11 @@ class RTC_LOCKABLE RTC_EXPORT TaskQueueBase { // Users of the TaskQueue should call Delete instead of directly deleting // this object. virtual ~TaskQueueBase() = default; + + private: + void PostTaskInternal(absl::AnyInvocable task, + const PostTaskTraits& traits, + const Location& location); }; struct TaskQueueDeleter { diff --git a/rtc_base/BUILD.gn b/rtc_base/BUILD.gn index 6fa8f0d392..6e885a9b1b 100644 --- a/rtc_base/BUILD.gn +++ b/rtc_base/BUILD.gn @@ -1453,6 +1453,21 @@ rtc_library("crc32") { absl_deps = [ "//third_party/abseil-cpp/absl/strings" ] } +rtc_library("voucher") { + sources = [ + "voucher.cc", + "voucher.h", + ] + deps = [ + ":macromagic", + ":refcount", + "../api:make_ref_counted", + "synchronization:mutex", + "system:rtc_export", + ] + absl_deps = [ "//third_party/abseil-cpp/absl/container:inlined_vector" ] +} + rtc_library("stream") { visibility = [ "*" ] sources = [ diff --git a/rtc_base/voucher.cc b/rtc_base/voucher.cc new file mode 100644 index 0000000000..46dd780334 --- /dev/null +++ b/rtc_base/voucher.cc @@ -0,0 +1,80 @@ +/* + * Copyright 2023 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 "rtc_base/voucher.h" + +#include +#include + +#include "api/make_ref_counted.h" + +namespace webrtc { +namespace { + +rtc::FinalRefCountedObject*& CurrentVoucherStorage() { + static thread_local rtc::FinalRefCountedObject* storage = nullptr; + return storage; +} + +} // namespace + +Voucher::ScopedSetter::ScopedSetter(Ptr voucher) + : old_current_(Voucher::Current()) { + Voucher::SetCurrent(std::move(voucher)); +} + +Voucher::ScopedSetter::~ScopedSetter() { + Voucher::SetCurrent(std::move(old_current_)); +} + +Voucher::Attachment::Id Voucher::Attachment::GetNextId() { + static std::atomic current_id = 0; + auto id = current_id.fetch_add(1); + RTC_CHECK(id < Voucher::kAttachmentCapacity); + return id; +} + +Voucher::Ptr Voucher::CurrentOrCreateForCurrentTask() { + auto& storage = CurrentVoucherStorage(); + Voucher::Ptr result(storage); + if (!result) { + result = rtc::make_ref_counted(); + storage = result.get(); + storage->AddRef(); + } + return result; +} + +Voucher::Ptr Voucher::Current() { + auto& storage = CurrentVoucherStorage(); + Voucher::Ptr result(storage); + return result; +} + +Voucher::Voucher() : attachments_(Voucher::kAttachmentCapacity) {} + +void Voucher::SetCurrent(Voucher::Ptr value) { + auto& storage = CurrentVoucherStorage(); + if (value.get() != storage) { + if (storage) { + storage->Release(); + } + storage = value.release(); + } +} + +void Voucher::SetAttachment(Attachment::Id id, + std::unique_ptr attachment) { + RTC_CHECK(id < kAttachmentCapacity); + MutexLock lock(&mu_); + attachments_[id] = std::move(attachment); +} + +} // namespace webrtc diff --git a/rtc_base/voucher.h b/rtc_base/voucher.h new file mode 100644 index 0000000000..30a6460906 --- /dev/null +++ b/rtc_base/voucher.h @@ -0,0 +1,78 @@ +/* + * Copyright 2023 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. + */ + +#ifndef RTC_BASE_VOUCHER_H_ +#define RTC_BASE_VOUCHER_H_ + +#include + +#include "absl/container/inlined_vector.h" +#include "rtc_base/ref_counted_object.h" +#include "rtc_base/synchronization/mutex.h" +#include "rtc_base/system/rtc_export.h" + +namespace webrtc { + +// A voucher is associated with a currently running task tree. Whenever tasks +// are posted, the current voucher is inherited and set as current in the new +// task. The voucher exists for as long as there are direct and indirect +// tasks running that descend from the task where the voucher was created. +class RTC_EXPORT Voucher { + public: + static constexpr size_t kAttachmentCapacity = 4; + + using Ptr = rtc::scoped_refptr>; + + // Vouchers aggregate attachments, which are application-specific attachments + // that have logic unrelated to the mechanics of Voucher progression. + class Attachment { + public: + using Id = size_t; + + // Attachments should call this function one to get an ID to use with + // SetAttachment. + static Attachment::Id GetNextId(); + + virtual ~Attachment() = default; + }; + + // Scoped setter that saves the current voucher on stack and instates a new + // one, until the scope exits. + class ScopedSetter { + public: + explicit ScopedSetter(Ptr voucher); + ~ScopedSetter(); + + private: + Ptr old_current_; + }; + + static Ptr Current(); + static Ptr CurrentOrCreateForCurrentTask(); + + // For Attachments: stores an attachment into a voucher. If one is already + // present, it gets replaced. + void SetAttachment(Attachment::Id id, std::unique_ptr attachment); + + private: + friend class rtc::FinalRefCountedObject; + Voucher(); + + friend class ScopedSetter; + static void SetCurrent(Ptr ptr); + + Mutex mu_; + absl::InlinedVector, kAttachmentCapacity> + attachments_ RTC_GUARDED_BY(&mu_); +}; + +} // namespace webrtc + +#endif // RTC_BASE_VOUCHER_H_ diff --git a/video/BUILD.gn b/video/BUILD.gn index 1722ad4f6e..850b4548d2 100644 --- a/video/BUILD.gn +++ b/video/BUILD.gn @@ -448,6 +448,7 @@ rtc_library("video_stream_encoder_impl") { "../rtc_base:safe_conversions", "../rtc_base:stringutils", "../rtc_base:timeutils", + "../rtc_base:voucher", "../rtc_base/experiments:balanced_degradation_settings", "../rtc_base/experiments:encoder_info_settings", "../rtc_base/experiments:field_trial_parser", diff --git a/video/video_stream_encoder.cc b/video/video_stream_encoder.cc index 2e5a120eed..c9bd604d05 100644 --- a/video/video_stream_encoder.cc +++ b/video/video_stream_encoder.cc @@ -49,7 +49,9 @@ #include "rtc_base/strings/string_builder.h" #include "rtc_base/system/no_unique_address.h" #include "rtc_base/thread_annotations.h" +#include "rtc_base/time_utils.h" #include "rtc_base/trace_event.h" +#include "rtc_base/voucher.h" #include "system_wrappers/include/metrics.h" #include "video/adaptation/video_stream_encoder_resource_manager.h" #include "video/alignment_adjuster.h" @@ -83,6 +85,35 @@ constexpr int kMaxAnimationPixels = 1280 * 720; constexpr int kDefaultMinScreenSharebps = 1200000; +// This voucher attachment measures the time from a passed capture reference +// time to the time when the voucher is destroyed. +class CaptureProcessingDurationMeasurement : public Voucher::Attachment { + public: + static void AttachToCurrentVoucher(Timestamp capture_reference_time) { + static const Voucher::Attachment::Id kCaptureToEncodeAttachmentId = + Voucher::Attachment::GetNextId(); + auto voucher = Voucher::CurrentOrCreateForCurrentTask(); + voucher->SetAttachment( + kCaptureToEncodeAttachmentId, + std::make_unique( + capture_reference_time)); + } + explicit CaptureProcessingDurationMeasurement( + Timestamp capture_reference_time) + : capture_reference_time_(capture_reference_time) {} + ~CaptureProcessingDurationMeasurement() override { + auto duration = + Clock::GetRealTimeClock()->CurrentTime() - capture_reference_time_; + TRACE_EVENT1("webrtc", "CaptureProcessingDurationMeasurement", "duration", + duration.us()); + RTC_HISTOGRAM_COUNTS_1000("WebRTC.Video.CaptureToSendTimeMs", + duration.ms()); + } + + private: + const Timestamp capture_reference_time_; +}; + int GetNumSpatialLayers(const VideoCodec& codec) { if (codec.codecType == kVideoCodecVP9) { return codec.VP9().numberOfSpatialLayers; @@ -2034,6 +2065,9 @@ void VideoStreamEncoder::EncodeVideoFrame(const VideoFrame& video_frame, frame_encode_metadata_writer_.OnEncodeStarted(out_frame); + CaptureProcessingDurationMeasurement::AttachToCurrentVoucher( + out_frame.reference_time().value_or(clock_->CurrentTime())); + const int32_t encode_status = encoder_->Encode(out_frame, &next_frame_types_); was_encode_called_since_last_initialization_ = true;