| // Copyright 2020 The Fuchsia 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 "src/media/audio/audio_core/activity_dispatcher.h" |
| |
| #include <optional> |
| |
| namespace media::audio { |
| namespace { |
| using fuchsia::media::AudioCaptureUsage; |
| using fuchsia::media::AudioRenderUsage; |
| |
| std::vector<AudioRenderUsage> ActivityToUsageVector( |
| const ActivityDispatcherImpl::RenderActivity& activity) { |
| std::vector<AudioRenderUsage> usage_vector; |
| usage_vector.reserve(activity.count()); |
| |
| for (int i = 0; i < fuchsia::media::RENDER_USAGE_COUNT; i++) { |
| if (activity[i]) { |
| usage_vector.push_back(static_cast<AudioRenderUsage>(i)); |
| } |
| } |
| return usage_vector; |
| } |
| |
| std::vector<AudioCaptureUsage> ActivityToUsageVector( |
| const ActivityDispatcherImpl::CaptureActivity& activity) { |
| std::vector<AudioCaptureUsage> usage_vector; |
| usage_vector.reserve(activity.count()); |
| |
| for (int i = 0; i < fuchsia::media::CAPTURE_USAGE_COUNT; i++) { |
| if (activity[i]) { |
| usage_vector.push_back(static_cast<AudioCaptureUsage>(i)); |
| } |
| } |
| return usage_vector; |
| } |
| } // namespace |
| |
| class ActivityDispatcherImpl::ActivityReporterImpl : public fuchsia::media::ActivityReporter { |
| public: |
| // The activity must outlive the ActivityReporterImpl. |
| explicit ActivityReporterImpl(const RenderActivity& last_known_render_activity, |
| const CaptureActivity& last_known_capture_activity, |
| fit::callback<void(ActivityReporterImpl*)> on_client_error); |
| ~ActivityReporterImpl() override; |
| |
| // Signal that the activity changed. |
| void OnRenderActivityChanged(); |
| void OnCaptureActivityChanged(); |
| |
| // Handle unresponsive client. |
| void OnClientError(); |
| |
| private: |
| // fuchsia::media::ActivityReporter. |
| void WatchRenderActivity(WatchRenderActivityCallback callback) override; |
| void WatchCaptureActivity(WatchCaptureActivityCallback callback) override; |
| |
| // Class to manage sending Activity updates to clients. |
| // An instance of Reporter can be specified to report on RenderActivity. |
| template <typename Activity, typename Callback> |
| class Reporter { |
| public: |
| Reporter(ActivityDispatcherImpl::ActivityReporterImpl& parent, const Activity& activity) |
| : parent_(parent), last_known_activity_(activity) {} |
| ~Reporter<Activity, Callback>() = default; |
| void WatchActivity(Callback callback); |
| void MaybeSendActivity(); |
| |
| private: |
| // Parent class to provide OnClientError(). |
| ActivityDispatcherImpl::ActivityReporterImpl& parent_; |
| // Last state known by the dispatcher. |
| const Activity& last_known_activity_; |
| // Last activity sent to client. |
| // Absent if no state was sent to the client yet. |
| std::optional<Activity> last_sent_activity_; |
| // If present, callback to call next time a state is available. |
| Callback waiting_callback_; |
| }; |
| |
| Reporter<RenderActivity, WatchRenderActivityCallback> render_reporter_; |
| Reporter<CaptureActivity, WatchCaptureActivityCallback> capture_reporter_; |
| // Called when the client has more than one hanging gets in flight. |
| fit::callback<void(ActivityReporterImpl*)> on_client_error_; |
| }; |
| |
| ActivityDispatcherImpl::ActivityDispatcherImpl() = default; |
| ActivityDispatcherImpl::~ActivityDispatcherImpl() = default; |
| |
| ActivityDispatcherImpl::ActivityReporterImpl::ActivityReporterImpl( |
| const RenderActivity& last_known_render_activity, |
| const CaptureActivity& last_known_capture_activity, |
| fit::callback<void(ActivityReporterImpl*)> on_client_error) |
| : render_reporter_(*this, last_known_render_activity), |
| capture_reporter_(*this, last_known_capture_activity), |
| on_client_error_(std::move(on_client_error)) {} |
| |
| ActivityDispatcherImpl::ActivityReporterImpl::~ActivityReporterImpl() = default; |
| |
| void ActivityDispatcherImpl::ActivityReporterImpl::OnRenderActivityChanged() { |
| render_reporter_.MaybeSendActivity(); |
| } |
| |
| void ActivityDispatcherImpl::ActivityReporterImpl::OnCaptureActivityChanged() { |
| capture_reporter_.MaybeSendActivity(); |
| } |
| |
| void ActivityDispatcherImpl::ActivityReporterImpl::OnClientError() { on_client_error_(this); } |
| |
| void ActivityDispatcherImpl::ActivityReporterImpl::WatchRenderActivity( |
| WatchRenderActivityCallback callback) { |
| render_reporter_.WatchActivity(std::move(callback)); |
| } |
| |
| void ActivityDispatcherImpl::ActivityReporterImpl::WatchCaptureActivity( |
| WatchCaptureActivityCallback callback) { |
| capture_reporter_.WatchActivity(std::move(callback)); |
| } |
| |
| template <typename Activity, typename Callback> |
| void ActivityDispatcherImpl::ActivityReporterImpl::Reporter<Activity, Callback>::WatchActivity( |
| Callback callback) { |
| // If there is more than one hanging get in flight, disconnect the client. |
| if (waiting_callback_) { |
| parent_.OnClientError(); |
| return; |
| } |
| |
| waiting_callback_ = std::move(callback); |
| MaybeSendActivity(); |
| } |
| |
| template <typename Activity, typename Callback> |
| void ActivityDispatcherImpl::ActivityReporterImpl::Reporter<Activity, |
| Callback>::MaybeSendActivity() { |
| // No request in flight. |
| if (!waiting_callback_) { |
| return; |
| } |
| |
| // No new update. |
| if (last_sent_activity_.has_value() && (last_sent_activity_.value() == last_known_activity_)) { |
| return; |
| } |
| |
| waiting_callback_(ActivityToUsageVector(last_known_activity_)); |
| last_sent_activity_ = last_known_activity_; |
| waiting_callback_ = nullptr; |
| } |
| |
| fidl::InterfaceRequestHandler<fuchsia::media::ActivityReporter> |
| ActivityDispatcherImpl::GetFidlRequestHandler() { |
| return fit::bind_member<&ActivityDispatcherImpl::Bind>(this); |
| } |
| |
| void ActivityDispatcherImpl::Bind( |
| fidl::InterfaceRequest<fuchsia::media::ActivityReporter> request) { |
| constexpr auto kEpitaphValue = ZX_ERR_PEER_CLOSED; |
| bindings_.AddBinding( |
| std::make_unique<ActivityReporterImpl>( |
| last_known_render_activity_, last_known_capture_activity_, |
| [this](ActivityReporterImpl* impl) { bindings_.CloseBinding(impl, kEpitaphValue); }), |
| std::move(request)); |
| } |
| |
| void ActivityDispatcherImpl::OnRenderActivityChanged(RenderActivity activity) { |
| last_known_render_activity_ = activity; |
| for (const auto& listener : bindings_.bindings()) { |
| listener->impl()->OnRenderActivityChanged(); |
| } |
| } |
| |
| void ActivityDispatcherImpl::OnCaptureActivityChanged(CaptureActivity activity) { |
| last_known_capture_activity_ = activity; |
| for (const auto& listener : bindings_.bindings()) { |
| listener->impl()->OnCaptureActivityChanged(); |
| } |
| } |
| } // namespace media::audio |