| // Copyright 2019 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. |
| |
| #ifndef SRC_MEDIA_AUDIO_AUDIO_CORE_REPORTER_H_ |
| #define SRC_MEDIA_AUDIO_AUDIO_CORE_REPORTER_H_ |
| |
| #include <fuchsia/media/cpp/fidl.h> |
| #include <lib/inspect_deprecated/component.h> |
| #include <lib/sys/cpp/component_context.h> |
| |
| #include <unordered_map> |
| |
| #ifndef ENABLE_REPORTER |
| #define ENABLE_REPORTER 1 |
| #endif |
| |
| namespace media::audio { |
| |
| #if ENABLE_REPORTER |
| |
| class AudioDevice; |
| class AudioRendererImpl; |
| class AudioCapturerImpl; |
| |
| // A singleton instance of |Reporter| handles instrumentation concerns (e.g. |
| // exposing information via inspect, cobalt, etc) for an audio_core instance. |
| // The idea is to make instrumentation as simple as possible for the code that |
| // does the real work. This class implements methods corresponding to the |
| // events that need to be reported in terms that make the most sense for the |
| // caller. Calls to those methods are made using the |REP| macro, which is |
| // notationally simple: |
| // |
| // REP(ThatThingHappened(details, more_details)); |
| // |
| // Use of the macro also allows instrumentation to be dropped from the build |
| // using a simple gn argument, if desired (by setting ENABLE_REPORTER to 0). |
| // The reporter is provisioned by calling its |Init| method, also using the |
| // |REP| macro. This is done in main.cc: |
| // |
| // REP(Init(component_context)); |
| // |
| // Note that calls to methods such as |AddingDevice| allocate resources, which |
| // should be deallocated by e.g. |RemovingDevice|. For the time being, this |
| // approach seems preferable to making client code hold RAII objects. |
| // |
| // For the time being, this class is single-threaded. Some support for multi- |
| // thread access will be required when traffic metrics are added. |
| // |
| // TODO(dalesat): Add traffic metrics. |
| // TODO(dalesat): Add cobalt logging. |
| class Reporter { |
| public: |
| static Reporter& Singleton(); |
| |
| const inspect_deprecated::Tree& tree() { return *inspector_->root_tree(); } |
| |
| //////////////////////////////////////////////////////////////////////////// |
| // The following methods are intended to be called using REP. For example: |
| // |
| // REP(Init(component_context)); |
| // |
| void Init(sys::ComponentContext* component_context); |
| |
| // Devices. |
| void FailedToOpenDevice(const std::string& name, bool is_input, int err); |
| void FailedToObtainFdioServiceChannel(const std::string& name, bool is_input, zx_status_t status); |
| void FailedToObtainStreamChannel(const std::string& name, bool is_input, zx_status_t status); |
| void AddingDevice(const std::string& name, const AudioDevice& device); |
| void RemovingDevice(const AudioDevice& device); |
| void DeviceStartupFailed(const AudioDevice& device); |
| void IgnoringDevice(const AudioDevice& device); |
| void ActivatingDevice(const AudioDevice& device); |
| void SettingDeviceGainInfo(const AudioDevice& device, |
| const fuchsia::media::AudioGainInfo& gain_info, uint32_t set_flags); |
| |
| // Renderers. |
| void AddingRenderer(const AudioRendererImpl& renderer); |
| void RemovingRenderer(const AudioRendererImpl& renderer); |
| void SettingRendererStreamType(const AudioRendererImpl& renderer, |
| const fuchsia::media::AudioStreamType& stream_type); |
| void AddingRendererPayloadBuffer(const AudioRendererImpl& renderer, uint32_t buffer_id, |
| uint64_t size); |
| void RemovingRendererPayloadBuffer(const AudioRendererImpl& renderer, uint32_t buffer_id); |
| void SendingRendererPacket(const AudioRendererImpl& renderer, |
| const fuchsia::media::StreamPacket& packet); |
| void SettingRendererGain(const AudioRendererImpl& renderer, float gain_db); |
| void SettingRendererGainWithRamp(const AudioRendererImpl& renderer, float gain_db, |
| zx_duration_t duration_ns, |
| fuchsia::media::audio::RampType ramp_type); |
| void SettingRendererMute(const AudioRendererImpl& renderer, bool muted); |
| void SettingRendererMinClockLeadTime(const AudioRendererImpl& renderer, |
| int64_t min_clock_lead_time_ns); |
| void SettingRendererPtsContinuityThreshold(const AudioRendererImpl& renderer, |
| float threshold_seconds); |
| |
| // Capturers. |
| void AddingCapturer(const AudioCapturerImpl& capturer); |
| void RemovingCapturer(const AudioCapturerImpl& capturer); |
| void SettingCapturerStreamType(const AudioCapturerImpl& capturer, |
| const fuchsia::media::AudioStreamType& stream_type); |
| void AddingCapturerPayloadBuffer(const AudioCapturerImpl& capturer, uint32_t buffer_id, |
| uint64_t size); |
| void SendingCapturerPacket(const AudioCapturerImpl& capturer, |
| const fuchsia::media::StreamPacket& packet); |
| void SettingCapturerGain(const AudioCapturerImpl& capturer, float gain_db); |
| void SettingCapturerGainWithRamp(const AudioCapturerImpl& capturer, float gain_db, |
| zx_duration_t duration_ns, |
| fuchsia::media::audio::RampType ramp_type); |
| void SettingCapturerMute(const AudioCapturerImpl& capturer, bool muted); |
| |
| private: |
| struct Device { |
| Device(inspect_deprecated::Node node) : node_(std::move(node)) { |
| gain_db_ = node_.CreateDoubleMetric("gain db", 0.0); |
| muted_ = node_.CreateUIntMetric("muted", 0); |
| agc_supported_ = node_.CreateUIntMetric("agc supported", 0); |
| agc_enabled_ = node_.CreateUIntMetric("agc enabled", 0); |
| } |
| inspect_deprecated::Node node_; |
| inspect_deprecated::DoubleMetric gain_db_; |
| inspect_deprecated::UIntMetric muted_; |
| inspect_deprecated::UIntMetric agc_supported_; |
| inspect_deprecated::UIntMetric agc_enabled_; |
| }; |
| |
| struct Output : public Device { |
| Output(inspect_deprecated::Node node) : Device(std::move(node)) {} |
| }; |
| |
| struct Input : public Device { |
| Input(inspect_deprecated::Node node) : Device(std::move(node)) {} |
| }; |
| |
| struct PayloadBuffer { |
| PayloadBuffer(inspect_deprecated::Node node, uint64_t size) : node_(std::move(node)) { |
| size_ = node_.CreateUIntMetric("size", size); |
| packets_ = node_.CreateUIntMetric("packets", 0); |
| } |
| |
| inspect_deprecated::Node node_; |
| inspect_deprecated::UIntMetric size_; |
| inspect_deprecated::UIntMetric packets_; |
| }; |
| |
| struct ClientPort { |
| ClientPort(inspect_deprecated::Node node) : node_(std::move(node)) { |
| sample_format_ = node_.CreateUIntMetric("sample format", 0); |
| channels_ = node_.CreateUIntMetric("channels", 0); |
| frames_per_second_ = node_.CreateUIntMetric("frames per second", 0); |
| payload_buffers_node_ = node_.CreateChild("payload buffers"); |
| gain_db_ = node_.CreateDoubleMetric("gain db", 0.0); |
| muted_ = node_.CreateUIntMetric("muted", 0); |
| set_gain_with_ramp_calls_ = node_.CreateUIntMetric("calls to SetGainWithRamp", 0); |
| } |
| inspect_deprecated::Node node_; |
| inspect_deprecated::UIntMetric sample_format_; |
| inspect_deprecated::UIntMetric channels_; |
| inspect_deprecated::UIntMetric frames_per_second_; |
| |
| inspect_deprecated::Node payload_buffers_node_; |
| std::unordered_map<uint32_t, PayloadBuffer> payload_buffers_; |
| |
| inspect_deprecated::DoubleMetric gain_db_; |
| inspect_deprecated::UIntMetric muted_; |
| |
| // We're just counting these calls for now. |SetGainWithRamp| isn't |
| // implemented and should never be called. |
| inspect_deprecated::UIntMetric set_gain_with_ramp_calls_; |
| }; |
| |
| struct Renderer : ClientPort { |
| Renderer(inspect_deprecated::Node node) : ClientPort(std::move(node)) { |
| min_clock_lead_time_ns_ = node_.CreateUIntMetric("min clock lead time (ns)", 0); |
| pts_continuity_threshold_seconds_ = |
| node_.CreateDoubleMetric("pts continuity threshold (s)", 0.0); |
| } |
| inspect_deprecated::UIntMetric min_clock_lead_time_ns_; |
| inspect_deprecated::DoubleMetric pts_continuity_threshold_seconds_; |
| }; |
| |
| struct Capturer : ClientPort { |
| Capturer(inspect_deprecated::Node node) : ClientPort(std::move(node)) {} |
| }; |
| |
| Device* FindOutput(const AudioDevice& device); |
| Device* FindInput(const AudioDevice& device); |
| Renderer* FindRenderer(const AudioRendererImpl& renderer); |
| Capturer* FindCapturer(const AudioCapturerImpl& capturer); |
| std::string NextRendererName(); |
| std::string NextCapturerName(); |
| |
| sys::ComponentContext* component_context_ = nullptr; |
| std::shared_ptr<inspect_deprecated::ComponentInspector> inspector_; |
| inspect_deprecated::UIntMetric failed_to_open_device_count_; |
| inspect_deprecated::UIntMetric failed_to_obtain_fdio_service_channel_count_; |
| inspect_deprecated::UIntMetric failed_to_obtain_stream_channel_count_; |
| inspect_deprecated::UIntMetric device_startup_failed_count_; |
| inspect_deprecated::Node outputs_node_; |
| inspect_deprecated::Node inputs_node_; |
| inspect_deprecated::Node renderers_node_; |
| inspect_deprecated::Node capturers_node_; |
| std::unordered_map<const AudioDevice*, Output> outputs_; |
| std::unordered_map<const AudioDevice*, Input> inputs_; |
| std::unordered_map<const AudioRendererImpl*, Renderer> renderers_; |
| std::unordered_map<const AudioCapturerImpl*, Capturer> capturers_; |
| uint64_t next_renderer_name_ = 0; |
| uint64_t next_capturer_name_ = 0; |
| }; |
| |
| #define REP(x) media::audio::Reporter::Singleton().x |
| #else // ENABLE_REPORTER |
| #define REP(x) (void)0 |
| #endif // ENABLE_REPORTER |
| |
| } // namespace media::audio |
| |
| #endif // SRC_MEDIA_AUDIO_AUDIO_CORE_REPORTER_H_ |