#ifndef SERVICES_AUDIO_OUTPUT_DEVICE_MIXER_IMPL_H_
#define SERVICES_AUDIO_OUTPUT_DEVICE_MIXER_IMPL_H_
#include <memory>
#include <set>
#include <string>
#include "base/check.h"
#include "base/containers/unique_ptr_adapters.h"
#include "base/dcheck_is_on.h"
#include "base/functional/callback.h"
#include "base/memory/raw_ptr.h"
#include "base/sequence_checker.h"
#include "base/synchronization/lock.h"
#include "base/timer/timer.h"
#include "media/audio/audio_io.h"
#include "media/base/audio_parameters.h"
#include "media/base/reentrancy_checker.h"
#include "services/audio/mixing_graph.h"
#include "services/audio/output_device_mixer.h"
namespace audio {
class MixingGraph;
class OutputDeviceMixerImpl final : public OutputDeviceMixer {
public:
using ErrorType = media::AudioOutputStream::AudioSourceCallback::ErrorType;
static constexpr base::TimeDelta kSwitchToUnmixedPlaybackDelay =
base::Seconds(1);
static constexpr double kDefaultVolume = 1.0;
OutputDeviceMixerImpl(
const std::string& device_id,
const media::AudioParameters& output_params,
MixingGraph::CreateCallback create_mixing_graph_callback,
CreateStreamCallback create_stream_callback);
OutputDeviceMixerImpl(const OutputDeviceMixerImpl&) = delete;
OutputDeviceMixerImpl& operator=(const OutputDeviceMixerImpl&) = delete;
~OutputDeviceMixerImpl() final;
media::AudioOutputStream* MakeMixableStream(
const media::AudioParameters& params,
base::OnceCallback<void()> on_device_change_callback) final;
void ProcessDeviceChange() final;
void StartListening(Listener* listener) final;
void StopListening(Listener* listener) final;
private:
class MixTrack;
class MixableOutputStream;
struct StreamAutoClose {
void operator()(media::AudioOutputStream* stream) {
if (!stream)
return;
stream->Close();
stream = nullptr;
}
};
enum class MixingError {
kNone = 0,
kOpenFailed,
kPlaybackFailed,
kMaxValue = kPlaybackFailed
};
using MixTracks =
std::set<std::unique_ptr<MixTrack>, base::UniquePtrComparator>;
using ActiveTracks = std::set<raw_ptr<MixTrack, SetExperimental>>;
using Listeners = std::set<raw_ptr<Listener, SetExperimental>>;
bool OpenStream(MixTrack* mix_track);
void StartStream(MixTrack* mix_track,
media::AudioOutputStream::AudioSourceCallback* callback);
void StopStream(MixTrack* mix_track);
void CloseStream(MixTrack* mix_track);
media::AudioOutputStream* CreateAndOpenDeviceStream(
const media::AudioParameters& params);
void BroadcastToListeners(const media::AudioBus& audio_bus,
base::TimeDelta delay);
void OnMixingGraphError(ErrorType error);
bool HasListeners() const;
bool MixingInProgress() const { return mixing_in_progress_; }
void EnsureMixingGraphOutputStreamOpen();
void StartMixingGraphPlayback();
void StopMixingGraphPlayback(MixingError mixing_error);
void SwitchToUnmixedPlaybackTimerHelper();
static const char* ErrorToString(MixingError error);
SEQUENCE_CHECKER(owning_sequence_);
const CreateStreamCallback create_stream_callback_;
MixTracks mix_tracks_ GUARDED_BY_CONTEXT(owning_sequence_);
ActiveTracks active_tracks_ GUARDED_BY_CONTEXT(owning_sequence_);
mutable base::Lock listener_lock_;
Listeners listeners_ GUARDED_BY(listener_lock_);
const media::AudioParameters mixing_graph_output_params_;
const std::unique_ptr<MixingGraph> mixing_graph_
GUARDED_BY_CONTEXT(owning_sequence_);
std::unique_ptr<media::AudioOutputStream, StreamAutoClose>
mixing_graph_output_stream_ GUARDED_BY_CONTEXT(owning_sequence_);
base::OneShotTimer switch_to_unmixed_playback_delay_timer_
GUARDED_BY_CONTEXT(owning_sequence_);
#if DCHECK_IS_ON()
bool device_changed_ = false;
#endif
REENTRANCY_CHECKER(reentrancy_checker_);
bool mixing_in_progress_ = false;
base::WeakPtrFactory<OutputDeviceMixerImpl> weak_factory_{this};
};
}
#endif