blob: d720c4148e2e3d75b5d614908a68ecc19a5e4a4b [file] [log] [blame]
// Copyright 2016 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_V1_AUDIO_OUTPUT_H_
#define SRC_MEDIA_AUDIO_AUDIO_CORE_V1_AUDIO_OUTPUT_H_
#include <lib/async/cpp/task.h>
#include <lib/async/cpp/time.h>
#include <lib/zx/time.h>
#include <optional>
#include "src/media/audio/audio_core/shared/device_config.h"
#include "src/media/audio/audio_core/shared/process_config.h"
#include "src/media/audio/audio_core/shared/reporter.h"
#include "src/media/audio/audio_core/v1/audio_device.h"
#include "src/media/audio/audio_core/v1/audio_driver.h"
#include "src/media/audio/audio_core/v1/clock.h"
#include "src/media/audio/audio_core/v1/output_pipeline.h"
#include "src/media/audio/lib/timeline/timeline_function.h"
namespace media::audio {
class Packet;
class EffectsLoaderV2;
class AudioOutput : public AudioDevice {
public:
~AudioOutput() override = default;
fpromise::promise<void, fuchsia::media::audio::UpdateEffectError> UpdateEffect(
const std::string& instance_name, const std::string& config) override;
// Replace the existing DeviceProfile and restart the OutputPipeline, for tuning purposes.
fpromise::promise<void, zx_status_t> UpdateDeviceProfile(
const DeviceConfig::OutputDeviceProfile::Parameters& params) override;
OutputPipeline* output_pipeline() const { return pipeline_.get(); }
// |media::audio::AudioDevice|
void SetGainInfo(const fuchsia::media::AudioGainInfo& info,
fuchsia::media::AudioGainValidFlags set_flags) override;
protected:
AudioOutput(const std::string& name, const DeviceConfig& config, ThreadingModel* threading_model,
DeviceRegistry* registry, LinkMatrix* link_matrix,
std::shared_ptr<AudioCoreClockFactory> clock_factory,
EffectsLoaderV2* effects_loader_v2, std::unique_ptr<AudioDriver>);
std::optional<Reporter::Container<Reporter::OutputDevice, Reporter::kObjectsToCache>::Ptr>&
reporter() {
return reporter_;
}
EffectsLoaderV2* effects_loader_v2() const { return effects_loader_v2_; }
// |media::audio::AudioObject|
//
// If we're initializing a source link, then we're connecting a renderer to this output. Else
// if we're initializing a dest link, then we're being connected as a loopback so we should return
// our loopback stream.
fpromise::result<std::pair<std::shared_ptr<Mixer>, ExecutionDomain*>, zx_status_t>
InitializeSourceLink(const AudioObject& source, std::shared_ptr<ReadableStream> stream) final;
void CleanupSourceLink(const AudioObject& source, std::shared_ptr<ReadableStream> stream) final;
fpromise::result<std::shared_ptr<ReadableStream>, zx_status_t> InitializeDestLink(
const AudioObject& dest) override;
// Mark this output as needing to be mixed at the specified future time.
// async PostForTime requires a time in the CLOCK_MONOTONIC timebase, so we use that here.
void SetNextSchedTimeMono(zx::time next_sched_time_mono) {
next_sched_time_mono_ = next_sched_time_mono;
}
inline void ClearNextSchedTime() { next_sched_time_mono_ = std::nullopt; }
void SetupMixTask(const DeviceConfig::OutputDeviceProfile& profile, size_t max_block_size_frames,
TimelineFunction device_reference_clock_to_fractional_frame)
FXL_EXCLUSIVE_LOCKS_REQUIRED(mix_domain().token());
virtual std::shared_ptr<OutputPipeline> CreateOutputPipeline(
const PipelineConfig& config, const VolumeCurve& volume_curve, size_t max_block_size_frames,
TimelineFunction device_reference_clock_to_fractional_frame,
std::shared_ptr<Clock> ref_clock);
void Cleanup() override FXL_EXCLUSIVE_LOCKS_REQUIRED(mix_domain().token());
struct FrameSpan {
int64_t start;
int64_t length;
bool is_mute;
};
void Process() FXL_EXCLUSIVE_LOCKS_REQUIRED(mix_domain().token());
void ProcessMixJob(ReadableStream::ReadLockContext& ctx, FrameSpan mix_span)
FXL_EXCLUSIVE_LOCKS_REQUIRED(mix_domain().token());
// Start mixing frames for a periodic mix job. This is called internally during the periodic mix
// task for this output. Implementations can control mix behavior in the following ways:
//
// If |std::nullopt| is returned, then no frames will be mixed. Instead all inputs will be trimmed
// such that any client audio packets that would have been fully consumed by the end of this mix
// job will be released. There will be no call to |FinishMixJob|.
//
// If the returned optional contains a FrameSpan with |is_mute| set to true, then no frames will
// be mixed. Instead all inputs will be trimmed such that any client audio packets that would have
// been fully consumed by the end of this mix job will be released. |WriteMixOutput| will be
// called to write silence. |FinishMixJob| will be called with the returned FrameSpan.
//
// If the returned optional contains a FrameSpan with |is_mute| set to false, then the mix
// pipeline will be advanced by the requested frame region. |WriteMixOutput| will be called one
// or more times to write the mixed output. |FinishMixJob| will be called with the returned
// FrameSpan.
virtual std::optional<AudioOutput::FrameSpan> StartMixJob(zx::time device_ref_time)
FXL_EXCLUSIVE_LOCKS_REQUIRED(mix_domain().token()) = 0;
// Writes frames in the given region. The start and end will intersect the FrameSpan returned from
// a prior |StartMixJob|. Write the given |payload|, or silence if |payload| is nullptr.
virtual void WriteMixOutput(int64_t start, int64_t length, const float* payload)
FXL_EXCLUSIVE_LOCKS_REQUIRED(mix_domain().token()) = 0;
// This is called at the end of a mix job to update internal state. |span| is the same span
// returned by the last call to StartMixJob.
virtual void FinishMixJob(const AudioOutput::FrameSpan& span)
FXL_EXCLUSIVE_LOCKS_REQUIRED(mix_domain().token()) = 0;
// The maximum amount of time it can take to run all pending mix jobs when a device
// wakes up to process pending jobs.
virtual zx::duration MixDeadline() const FXL_EXCLUSIVE_LOCKS_REQUIRED(mix_domain().token()) = 0;
private:
// Timer used to schedule periodic mixing.
void MixTimerThunk() {
OBTAIN_EXECUTION_DOMAIN_TOKEN(token, &mix_domain());
Process();
}
async::TaskClosureMethod<AudioOutput, &AudioOutput::MixTimerThunk> mix_timer_
FXL_GUARDED_BY(mix_domain().token()){this};
std::optional<zx::time> next_sched_time_mono_;
size_t max_block_size_frames_;
std::shared_ptr<OutputPipeline> pipeline_;
std::optional<Reporter::Container<Reporter::OutputDevice, Reporter::kObjectsToCache>::Ptr>
reporter_;
EffectsLoaderV2* effects_loader_v2_;
};
} // namespace media::audio
#endif // SRC_MEDIA_AUDIO_AUDIO_CORE_V1_AUDIO_OUTPUT_H_