| // 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::AudioRenderUsage; |
| |
| std::vector<AudioRenderUsage> ActivityToUsageVector( |
| const ActivityDispatcherImpl::Activity& activity) { |
| std::vector<AudioRenderUsage> 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<AudioRenderUsage>(i)); |
| } |
| } |
| return usage_vector; |
| } |
| } // namespace |
| |
| class ActivityDispatcherImpl::ActivityReporterImpl : public fuchsia::media::ActivityReporter { |
| public: |
| // The activity must outlive the ActivityReporterImpl. |
| explicit ActivityReporterImpl(const Activity& last_known_activity, |
| fit::callback<void(ActivityReporterImpl*)> on_client_error); |
| ~ActivityReporterImpl() override; |
| |
| // Signal that the activity changed. |
| void OnActivityChanged(); |
| |
| private: |
| // fuchsia::media::ActivityReporter. |
| void WatchRenderActivity(WatchRenderActivityCallback callback) override; |
| // Send Activity if there was some update. |
| void MaybeSendActivity(); |
| |
| // 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. |
| WatchRenderActivityCallback waiting_callback_; |
| // 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 Activity& last_known_activity, fit::callback<void(ActivityReporterImpl*)> on_client_error) |
| : last_known_activity_(last_known_activity), on_client_error_(std::move(on_client_error)) {} |
| |
| ActivityDispatcherImpl::ActivityReporterImpl::~ActivityReporterImpl() = default; |
| |
| void ActivityDispatcherImpl::ActivityReporterImpl::OnActivityChanged() { MaybeSendActivity(); } |
| |
| void ActivityDispatcherImpl::ActivityReporterImpl::WatchRenderActivity( |
| WatchRenderActivityCallback callback) { |
| // If there is more than one hanging get in flight, disconnect the client. |
| if (waiting_callback_) { |
| on_client_error_(this); |
| return; |
| } |
| |
| waiting_callback_ = std::move(callback); |
| MaybeSendActivity(); |
| } |
| |
| void ActivityDispatcherImpl::ActivityReporterImpl::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(this, &ActivityDispatcherImpl::Bind); |
| } |
| |
| void ActivityDispatcherImpl::Bind( |
| fidl::InterfaceRequest<fuchsia::media::ActivityReporter> request) { |
| constexpr auto kEpitaphValue = ZX_ERR_PEER_CLOSED; |
| bindings_.AddBinding( |
| std::make_unique<ActivityReporterImpl>( |
| last_known_activity_, |
| [this](ActivityReporterImpl* impl) { bindings_.CloseBinding(impl, kEpitaphValue); }), |
| std::move(request)); |
| } |
| |
| void ActivityDispatcherImpl::OnActivityChanged(Activity activity) { |
| last_known_activity_ = activity; |
| for (const auto& listener : bindings_.bindings()) { |
| listener->impl()->OnActivityChanged(); |
| } |
| } |
| } // namespace media::audio |