blob: d9f7d522d387ec2f8d39e495d7e46ec15511c847 [file] [log] [blame]
// Copyright 2015 The Chromium 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 CHROMECAST_MEDIA_CMA_BACKEND_MIXER_STREAM_MIXER_H_
#define CHROMECAST_MEDIA_CMA_BACKEND_MIXER_STREAM_MIXER_H_
#include <stdint.h>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "base/callback.h"
#include "base/containers/flat_map.h"
#include "base/containers/flat_set.h"
#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "base/memory/weak_ptr.h"
#include "base/single_thread_task_runner.h"
#include "base/threading/sequence_bound.h"
#include "base/threading/thread.h"
#include "base/time/time.h"
#include "chromecast/media/cma/backend/mixer/loopback_handler.h"
#include "chromecast/media/cma/backend/mixer/mixer_control.h"
#include "chromecast/media/cma/backend/mixer/mixer_input.h"
#include "chromecast/media/cma/backend/mixer/mixer_pipeline.h"
#include "chromecast/public/cast_media_shlib.h"
#include "chromecast/public/media/external_audio_pipeline_shlib.h"
#include "chromecast/public/media/media_pipeline_backend.h"
#include "chromecast/public/volume_control.h"
namespace chromecast {
class ThreadHealthChecker;
namespace media {
class AudioOutputRedirector;
class InterleavedChannelMixer;
class MixerServiceReceiver;
class MixerOutputStream;
class PostProcessingPipelineFactory;
// Mixer implementation. The mixer has zero or more inputs; these can be added
// or removed at any time. The mixer will pull from each input source whenever
// it needs more data; input sources provide data if available, and return the
// number of frames filled. Any unfilled frames will be filled with silence;
// the mixer always outputs audio when active, even if input sources underflow.
// The MixerInput class wraps each source and provides volume control and
// resampling to the output rate. The mixer will pass the current rendering
// delay to each source when it requests data; this delay indicates when the new
// data will play out of the speaker (the newly filled data will play out at
// delay.timestamp + delay.delay).
//
// The mixer chooses its output sample rate based on the sample rates of the
// current sources (unless the output sample rate is fixed via the
// --audio-output-sample-rate command line flag). When a new source is added:
// * If the mixer was not running, it is started with the output sample rate
// set to the input sample rate of the new source.
// * If the mixer previously had no input sources, the output sample rate is
// updated to match the input sample rate of the new source.
// * If the new input source is primary, and the mixer has no other primary
// input sources, then the output sample rate is updated to match the input
// sample rate of the new source.
// * Otherwise, the output sample rate remains unchanged.
class StreamMixer : public MixerControl {
public:
// Returns the mixer instance for this process. Caller must not delete the
// returned instance!
static StreamMixer* Get();
StreamMixer();
// Only public to allow tests to create/destroy mixers.
~StreamMixer() override;
int num_output_channels() const { return num_output_channels_; }
// Adds an input source to the mixer. The |input_source| must live at least
// until input_source->FinalizeAudioPlayback() is called.
void AddInput(MixerInput::Source* input_source);
// Removes an input source from the mixer. The input source will be removed
// asynchronously. Once it has been removed, FinalizeAudioPlayback() will be
// called on it; at this point it is safe to delete the input source.
void RemoveInput(MixerInput::Source* input_source);
// Adds/removes a loopback audio observer. Observers are passed audio data
// just before it is written to output (prior to linearization filters).
void AddLoopbackAudioObserver(
CastMediaShlib::LoopbackAudioObserver* observer);
void RemoveLoopbackAudioObserver(
CastMediaShlib::LoopbackAudioObserver* observer);
// Adds/removes an output redirector. Output redirectors take audio from
// matching inputs and pass them to secondary output instead of the normal
// mixer output.
void AddAudioOutputRedirector(
std::unique_ptr<AudioOutputRedirector> redirector);
void RemoveAudioOutputRedirector(AudioOutputRedirector* redirector);
void ModifyAudioOutputRedirection(
AudioOutputRedirector* redirector,
std::vector<std::pair<AudioContentType, std::string>>
stream_match_patterns);
// Sets the volume multiplier for the given content |type|.
void SetVolume(AudioContentType type, float level);
// Sets the mute state for the given content |type|.
void SetMuted(AudioContentType type, bool muted);
// Sets the volume multiplier limit for the given content |type|.
void SetOutputLimit(AudioContentType type, float limit);
// Sets the per-stream volume multiplier for |source|.
void SetVolumeMultiplier(MixerInput::Source* source, float multiplier);
// Sends configuration string |config| to processor |name|.
void SetPostProcessorConfig(const std::string& name,
const std::string& config);
void ResetPostProcessors(CastMediaShlib::ResultCallback callback);
// Test-only methods.
StreamMixer(std::unique_ptr<MixerOutputStream> output,
std::unique_ptr<base::Thread> mixer_thread,
scoped_refptr<base::SingleThreadTaskRunner> mixer_task_runner);
void ResetPostProcessorsForTest(
std::unique_ptr<PostProcessingPipelineFactory> pipeline_factory,
const std::string& pipeline_json);
void SetNumOutputChannelsForTest(int num_output_channels);
void EnableDynamicChannelCountForTest(bool enable);
private:
class BaseExternalMediaVolumeChangeRequestObserver
: public ExternalAudioPipelineShlib::
ExternalMediaVolumeChangeRequestObserver {
public:
~BaseExternalMediaVolumeChangeRequestObserver() override = default;
};
class ExternalMediaVolumeChangeRequestObserver;
enum State {
kStateStopped,
kStateRunning,
};
// Contains volume control information for an audio content type.
struct VolumeInfo {
float GetEffectiveVolume();
float volume = 0.0f;
float limit = 1.0f;
bool muted = false;
};
// MixerControl implementation:
void SetNumOutputChannels(int num_channels) override;
void SetNumOutputChannelsOnThread(int num_channels);
void ResetPostProcessorsOnThread(CastMediaShlib::ResultCallback callback,
const std::string& override_config);
void CreatePostProcessors(CastMediaShlib::ResultCallback callback,
const std::string& override_config,
int expected_input_channels);
void FinalizeOnMixerThread();
void Start();
void Stop();
void CheckChangeOutputParams(int num_input_channels,
int input_samples_per_second);
void SignalError(MixerInput::Source::MixerError error);
int GetEffectiveChannelCount(MixerInput::Source* input_source);
void RemoveInputOnThread(MixerInput::Source* input_source);
void SetCloseTimeout();
void UpdatePlayoutChannel();
void PlaybackLoop();
void WriteOneBuffer();
void WriteMixedPcm(int frames, int64_t expected_playback_time);
void RemoveAudioOutputRedirectorOnThread(AudioOutputRedirector* redirector);
int GetSampleRateForDeviceId(const std::string& device);
MediaPipelineBackend::AudioDecoder::RenderingDelay GetTotalRenderingDelay(
FilterGroup* filter_group);
std::unique_ptr<MixerOutputStream> output_;
std::unique_ptr<PostProcessingPipelineFactory>
post_processing_pipeline_factory_;
std::unique_ptr<MixerPipeline> mixer_pipeline_;
std::unique_ptr<base::Thread> mixer_thread_;
scoped_refptr<base::SingleThreadTaskRunner> mixer_task_runner_;
std::unique_ptr<base::Thread> io_thread_;
scoped_refptr<base::SingleThreadTaskRunner> io_task_runner_;
std::unique_ptr<LoopbackHandler, LoopbackHandler::Deleter> loopback_handler_;
std::unique_ptr<ThreadHealthChecker> health_checker_;
std::unique_ptr<InterleavedChannelMixer> loopback_channel_mixer_;
std::unique_ptr<InterleavedChannelMixer> output_channel_mixer_;
void OnHealthCheckFailed();
bool enable_dynamic_channel_count_;
const int low_sample_rate_cutoff_;
int fixed_num_output_channels_;
const int fixed_output_sample_rate_;
const base::TimeDelta no_input_close_timeout_;
// Force data to be filtered in multiples of |filter_frame_alignment_| frames.
// Must be a multiple of 4 for some NEON implementations. Some
// AudioPostProcessors require stricter alignment conditions.
const int filter_frame_alignment_;
int playout_channel_ = kChannelAll;
int requested_input_channels_ = 0;
int post_processor_input_channels_ = 0;
int num_output_channels_ = 0;
int requested_output_samples_per_second_ = 0;
int output_samples_per_second_ = 0;
int frames_per_write_ = 0;
int redirector_samples_per_second_ = 0;
int redirector_frames_per_write_ = 0;
State state_;
base::TimeTicks close_timestamp_;
base::RepeatingClosure playback_loop_task_;
base::flat_map<MixerInput::Source*, std::unique_ptr<MixerInput>> inputs_;
base::flat_map<MixerInput::Source*, std::unique_ptr<MixerInput>>
ignored_inputs_;
base::flat_map<AudioContentType, VolumeInfo> volume_info_;
base::flat_map<AudioOutputRedirector*, std::unique_ptr<AudioOutputRedirector>>
audio_output_redirectors_;
const bool external_audio_pipeline_supported_;
std::unique_ptr<BaseExternalMediaVolumeChangeRequestObserver>
external_volume_observer_;
base::SequenceBound<MixerServiceReceiver> receiver_;
base::WeakPtrFactory<StreamMixer> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(StreamMixer);
};
} // namespace media
} // namespace chromecast
#endif // CHROMECAST_MEDIA_CMA_BACKEND_MIXER_STREAM_MIXER_H_