| Index: chromecast/media/cma/backend/alsa/filter_group.cc
|
| diff --git a/chromecast/media/cma/backend/alsa/filter_group.cc b/chromecast/media/cma/backend/alsa/filter_group.cc
|
| index e041b7e9b8ef6c6ea5a07d1985896d1cfbdab37a..2e798fe5327ec5df6f7d77030a5cc0cda38d770e 100644
|
| --- a/chromecast/media/cma/backend/alsa/filter_group.cc
|
| +++ b/chromecast/media/cma/backend/alsa/filter_group.cc
|
| @@ -7,24 +7,27 @@
|
| #include <algorithm>
|
|
|
| #include "base/memory/ptr_util.h"
|
| +#include "base/time/time.h"
|
| #include "base/values.h"
|
| #include "chromecast/media/cma/backend/alsa/post_processing_pipeline.h"
|
| #include "media/base/audio_bus.h"
|
| +#include "media/base/vector_math.h"
|
|
|
| namespace chromecast {
|
| namespace media {
|
| -FilterGroup::FilterGroup(const std::unordered_set<std::string>& input_types,
|
| - AudioContentType content_type,
|
| - int num_channels,
|
| - const base::ListValue* filter_list)
|
| - : input_types_(input_types),
|
| - content_type_(content_type),
|
| - num_channels_(num_channels),
|
| +FilterGroup::FilterGroup(int num_channels,
|
| + const std::string& name,
|
| + const base::ListValue* filter_list,
|
| + const std::unordered_set<std::string>& device_ids,
|
| + const std::vector<FilterGroup*>& mixed_inputs)
|
| + : num_channels_(num_channels),
|
| + name_(name),
|
| + device_ids_(device_ids),
|
| + mixed_inputs_(mixed_inputs),
|
| output_samples_per_second_(0),
|
| channels_(num_channels_),
|
| post_processing_pipeline_(
|
| - base::MakeUnique<PostProcessingPipeline>(filter_list,
|
| - num_channels_)) {}
|
| + PostProcessingPipeline::Create(name_, filter_list, num_channels_)) {}
|
|
|
| FilterGroup::~FilterGroup() = default;
|
|
|
| @@ -34,27 +37,46 @@ void FilterGroup::Initialize(int output_samples_per_second) {
|
| }
|
|
|
| bool FilterGroup::CanProcessInput(StreamMixerAlsa::InputQueue* input) {
|
| - return !(input_types_.find(input->device_id()) == input_types_.end());
|
| + return !(device_ids_.find(input->device_id()) == device_ids_.end());
|
| }
|
|
|
| void FilterGroup::AddActiveInput(StreamMixerAlsa::InputQueue* input) {
|
| active_inputs_.push_back(input);
|
| }
|
|
|
| -std::vector<uint8_t>* FilterGroup::GetInterleaved() {
|
| - return &interleaved_;
|
| -}
|
| -
|
| -bool FilterGroup::MixAndFilter(int chunk_size) {
|
| +float FilterGroup::MixAndFilter(int chunk_size) {
|
| DCHECK_NE(output_samples_per_second_, 0);
|
| - if (active_inputs_.empty() && !post_processing_pipeline_->IsRinging()) {
|
| - return false; // Output will be silence, no need to mix.
|
| - }
|
|
|
| ResizeBuffersIfNecessary(chunk_size);
|
|
|
| - mixed_->ZeroFramesPartial(0, chunk_size);
|
| float volume = 0.0f;
|
| +
|
| + // Recursively mix inputs.
|
| + for (auto* filter_group : mixed_inputs_) {
|
| + volume = std::max(volume, filter_group->MixAndFilter(chunk_size));
|
| + }
|
| +
|
| + // |volume| can only be 0 if no |mixed_inputs_| have data.
|
| + // This is true because FilterGroup can only return 0 if:
|
| + // a) It has no data and its PostProcessorPipeline is not ringing.
|
| + // (early return, below) or
|
| + // b) The output volume is 0 and has NEVER been non-zero,
|
| + // since FilterGroup will use last_volume_ if volume is 0.
|
| + // In this case, there was never any data in the pipeline.
|
| + if (active_inputs_.empty() && volume == 0.0f &&
|
| + !post_processing_pipeline_->IsRinging()) {
|
| + if (frames_zeroed_ < chunk_size) {
|
| + // Ensure mixed_ is zeros. This is necessary if |mixed_| is read later.
|
| + mixed_->ZeroFramesPartial(0, chunk_size);
|
| + frames_zeroed_ = chunk_size;
|
| + }
|
| + return 0.0f; // Output will be silence, no need to mix.
|
| + }
|
| +
|
| + frames_zeroed_ = 0;
|
| +
|
| + // Mix InputQueues
|
| + mixed_->ZeroFramesPartial(0, chunk_size);
|
| for (StreamMixerAlsa::InputQueue* input : active_inputs_) {
|
| input->GetResampledData(temp_.get(), chunk_size);
|
| for (int c = 0; c < num_channels_; ++c) {
|
| @@ -65,18 +87,37 @@ bool FilterGroup::MixAndFilter(int chunk_size) {
|
| volume = std::max(volume, input->EffectiveVolume());
|
| }
|
|
|
| - post_processing_pipeline_->ProcessFrames(channels_, chunk_size, volume,
|
| - active_inputs_.empty());
|
| - mixed_->ToInterleaved(chunk_size, BytesPerOutputFormatSample(),
|
| - interleaved_.data());
|
| - return true;
|
| + // Mix FilterGroups
|
| + for (FilterGroup* group : mixed_inputs_) {
|
| + if (group->last_volume() > 0.0f) {
|
| + for (int c = 0; c < num_channels_; ++c) {
|
| + ::media::vector_math::FMAC(group->data()->channel(c), 1.0f, chunk_size,
|
| + channels_[c]);
|
| + }
|
| + }
|
| + }
|
| +
|
| + bool is_silence = (volume == 0.0f);
|
| +
|
| + // Allow paused streams to "ring out" at the last valid volume.
|
| + // If the stream volume is actually 0, this doesn't matter, since the
|
| + // data is 0's anyway.
|
| + if (!is_silence) {
|
| + last_volume_ = volume;
|
| + }
|
| +
|
| + delay_frames_ = post_processing_pipeline_->ProcessFrames(
|
| + channels_, chunk_size, last_volume_, is_silence);
|
| + return last_volume_;
|
| }
|
|
|
| -void FilterGroup::ClearInterleaved(int chunk_size) {
|
| - ResizeBuffersIfNecessary(chunk_size);
|
| - memset(interleaved_.data(), 0,
|
| - static_cast<size_t>(chunk_size) * num_channels_ *
|
| - BytesPerOutputFormatSample());
|
| +int64_t FilterGroup::GetRenderingDelayMicroseconds() {
|
| + return delay_frames_ * base::Time::kMicrosecondsPerSecond /
|
| + output_samples_per_second_;
|
| +}
|
| +
|
| +void FilterGroup::ClearActiveInputs() {
|
| + active_inputs_.clear();
|
| }
|
|
|
| void FilterGroup::ResizeBuffersIfNecessary(int chunk_size) {
|
| @@ -89,26 +130,6 @@ void FilterGroup::ResizeBuffersIfNecessary(int chunk_size) {
|
| if (!temp_ || temp_->frames() < chunk_size) {
|
| temp_ = ::media::AudioBus::Create(num_channels_, chunk_size);
|
| }
|
| -
|
| - size_t interleaved_size = static_cast<size_t>(chunk_size) * num_channels_ *
|
| - BytesPerOutputFormatSample();
|
| -
|
| - if (interleaved_.size() < interleaved_size) {
|
| - interleaved_.resize(interleaved_size);
|
| - }
|
| -}
|
| -
|
| -int FilterGroup::BytesPerOutputFormatSample() {
|
| - return sizeof(int32_t);
|
| -}
|
| -
|
| -void FilterGroup::ClearActiveInputs() {
|
| - active_inputs_.clear();
|
| -}
|
| -
|
| -void FilterGroup::DisablePostProcessingForTest() {
|
| - post_processing_pipeline_ =
|
| - base::MakeUnique<PostProcessingPipeline>(nullptr, num_channels_);
|
| }
|
|
|
| } // namespace media
|
|
|