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;