[go: nahoru, domu]

blob: 8b479831a2fa8ab7601b76cccf66486dbce2d914 [file] [log] [blame]
// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#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/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;
// OutputDeviceMixerImpl manages the rendering of all output streams created by
// MakeMixableStream(). It has two rendering modes: if there
// are not listeners attached, each active output stream is rendered
// independently. If the mixer has listeners and at least one of the managed
// streams there is playing, all output streams are mixed by
// |mixing_graph_| and the result is played by |mixing_graph_output_stream_|.
// After mixing started, the mixed playback will stop only when the last
// listener is gone; this means the mixed playback will continue even when there
// are no managed streams playing. That is done because the listeners do not
// like when the reference signal stops and then starts again: it can cause
// reference signal delay jumps. TODO(olka): add metrics for how often/how long
// we may have listeners forsing such silent mixed playback.
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;
// |device_id| is the id of the device to manage the playback for (expected to
// be a valid physical output device id); |output_params| are the parameters
// of the audio mix the mixer will be playing in case there are listeners
// attached (otherwise, each output stream is played independently using its
// individual parameters); |create_mixing_graph_callback| is used to create
// a mixing graph object which will combine individual audio streams into a
// mixed signal; |create_stream_callback| is used to create physical output
// streams to be used for audio rendering.
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;
// Creates an output stream managed by |this|.
media::AudioOutputStream* MakeMixableStream(
const media::AudioParameters& params,
base::OnceCallback<void()> on_device_change_callback) final;
void ProcessDeviceChange() final;
// ReferenceOutput implementation.
// Starts listening to the mixed audio stream.
void StartListening(Listener* listener) final;
// Stops listening to the mixed audio stream.
void StopListening(Listener* listener) final;
private:
class MixTrack;
class MixableOutputStream;
class MixingStats;
struct StreamAutoClose {
void operator()(media::AudioOutputStream* stream) {
if (!stream)
return;
stream->Close();
stream = nullptr;
}
};
// Do not change: used for UMA reporting, matches
// AudioOutputDeviceMixerMixedPlaybackStatus from enums.xml.
enum class MixingError {
kNone = 0,
kOpenFailed,
kPlaybackFailed,
kMaxValue = kPlaybackFailed
};
using MixTracks =
std::set<std::unique_ptr<MixTrack>, base::UniquePtrComparator>;
using ActiveTracks = std::set<MixTrack*>;
using Listeners = std::set<Listener*>;
// Operations delegated by MixableOutputStream.
bool OpenStream(MixTrack* mix_track);
void StartStream(MixTrack* mix_track,
media::AudioOutputStream::AudioSourceCallback* callback);
void StopStream(MixTrack* mix_track);
void CloseStream(MixTrack* mix_track);
// Helper to create physical audio streams.
media::AudioOutputStream* CreateAndOpenDeviceStream(
const media::AudioParameters& params);
// Delivers audio to listeners; provided as a callback to MixingGraph.
void BroadcastToListeners(const media::AudioBus& audio_bus,
base::TimeDelta delay);
// Processes |mixing_output_stream_| rendering errors; provided as a callback
// to MixingGraph.
void OnMixingGraphError(ErrorType error);
// Helpers to manage audio playback.
bool HasListeners() const;
bool MixingInProgress() const { return !!mixing_session_stats_; }
void EnsureMixingGraphOutputStreamOpen();
void StartMixingGraphPlayback();
void StopMixingGraphPlayback(MixingError mixing_error);
void SwitchToUnmixedPlaybackTimerHelper();
static const char* ErrorToString(MixingError error);
SEQUENCE_CHECKER(owning_sequence_);
// Callback to create physical audio streams.
const CreateStreamCallback create_stream_callback_;
// Streams managed by the mixer.
MixTracks mix_tracks_ GUARDED_BY_CONTEXT(owning_sequence_);
// Active tracks rendering audio.
ActiveTracks active_tracks_ GUARDED_BY_CONTEXT(owning_sequence_);
// Listeners receiving the reference output (mixed playback). Playback is
// mixed if and only if there is at least one listener.
mutable base::Lock listener_lock_;
Listeners listeners_ GUARDED_BY(listener_lock_);
// Audio parameters for mixed playback.
const media::AudioParameters mixing_graph_output_params_;
// Provides the audio mix combined of |active_members_| audio to
// |mixing_output_stream_| for playback.
const std::unique_ptr<MixingGraph> mixing_graph_
GUARDED_BY_CONTEXT(owning_sequence_);
// Stream to render the audio mix. Non-null if and only if the playback is
// being mixed at the moment.
std::unique_ptr<media::AudioOutputStream, StreamAutoClose>
mixing_graph_output_stream_ GUARDED_BY_CONTEXT(owning_sequence_);
// Delays switching to unmixed playback in case a new listener is coming
// soon (within kSwitchToIndependentPlaybackDelay).
base::OneShotTimer switch_to_unmixed_playback_delay_timer_
GUARDED_BY_CONTEXT(owning_sequence_);
// Non-null when the playback is being mixed. Collects mixing statistics.
// Logs them upon the destruction when mixing stops. Non-null while mixing
// is in progress, and is used as an indicator of that.
std::unique_ptr<MixingStats> mixing_session_stats_;
#if DCHECK_IS_ON()
bool device_changed_ = false;
#endif
// A mixable stream operation cannot be invoked within a context of another
// such operation. In practice it means that AudioOutputStream created by the
// mixer cannot be stopped/closed synchronously from AudioSourceCallback
// provided to it on AudioOutputStream::Start().
REENTRANCY_CHECKER(reentrancy_checker_);
// Supplies weak pointers to |this| for MixableOutputStream instances.
base::WeakPtrFactory<OutputDeviceMixerImpl> weak_factory_{this};
};
} // namespace audio
#endif // SERVICES_AUDIO_OUTPUT_DEVICE_MIXER_IMPL_H_