blob: 3c377bc84bf4d238df08bbe89614e476646a50a3 [file] [log] [blame]
// Copyright 2018 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.
#include "chromecast/media/cma/backend/mixer_pipeline.h"
#include <utility>
#include "base/containers/flat_set.h"
#include "base/logging.h"
#include "chromecast/media/base/audio_device_ids.h"
#include "chromecast/media/cma/backend/filter_group.h"
#include "chromecast/media/cma/backend/post_processing_pipeline_impl.h"
#include "chromecast/media/cma/backend/post_processing_pipeline_parser.h"
#include "media/audio/audio_device_description.h"
namespace chromecast {
namespace media {
namespace {
const int kNumInputChannels = 2;
bool IsOutputDeviceId(const std::string& device) {
return device == ::media::AudioDeviceDescription::kDefaultDeviceId ||
device == ::media::AudioDeviceDescription::kCommunicationsDeviceId ||
device == kLocalAudioDeviceId || device == kAlarmAudioDeviceId ||
device == kPlatformAudioDeviceId /* e.g. bluetooth and aux */ ||
device == kTtsAudioDeviceId || device == kBypassAudioDeviceId;
}
std::unique_ptr<FilterGroup> CreateFilterGroup(
int input_channels,
const std::string& name,
const base::Value* filter_list,
PostProcessingPipelineFactory* ppp_factory) {
DCHECK(ppp_factory);
auto pipeline =
ppp_factory->CreatePipeline(name, filter_list, input_channels);
return std::make_unique<FilterGroup>(input_channels, name,
std::move(pipeline));
}
} // namespace
// static
std::unique_ptr<MixerPipeline> MixerPipeline::CreateMixerPipeline(
PostProcessingPipelineParser* config,
PostProcessingPipelineFactory* factory) {
std::unique_ptr<MixerPipeline> mixer_pipeline(new MixerPipeline);
if (mixer_pipeline->BuildPipeline(config, factory)) {
return mixer_pipeline;
}
return nullptr;
}
MixerPipeline::MixerPipeline() = default;
MixerPipeline::~MixerPipeline() = default;
bool MixerPipeline::BuildPipeline(PostProcessingPipelineParser* config,
PostProcessingPipelineFactory* factory) {
DCHECK(config);
DCHECK(factory);
int mix_group_input_channels = -1;
// Create "stream" processor groups:
for (auto& stream_pipeline : config->GetStreamPipelines()) {
const base::Value* device_ids = stream_pipeline.stream_types;
DCHECK(!device_ids->GetList().empty());
DCHECK(device_ids->GetList()[0].is_string());
filter_groups_.push_back(CreateFilterGroup(
kNumInputChannels, device_ids->GetList()[0].GetString() /* name */,
stream_pipeline.pipeline, factory));
if (!SetGroupDeviceIds(device_ids, filter_groups_.back().get())) {
return false;
}
if (mix_group_input_channels == -1) {
mix_group_input_channels = filter_groups_.back()->GetOutputChannelCount();
} else if (mix_group_input_channels !=
filter_groups_.back()->GetOutputChannelCount()) {
LOG(ERROR)
<< "All output stream mixers must have the same number of channels"
<< filter_groups_.back()->name() << " has "
<< filter_groups_.back()->GetOutputChannelCount()
<< " but others have " << mix_group_input_channels;
return false;
}
}
if (mix_group_input_channels == -1) {
mix_group_input_channels = kNumInputChannels;
}
// Create "mix" processor group:
const auto mix_pipeline = config->GetMixPipeline();
std::unique_ptr<FilterGroup> mix_filter = CreateFilterGroup(
mix_group_input_channels, "mix", mix_pipeline.pipeline, factory);
for (std::unique_ptr<FilterGroup>& group : filter_groups_) {
mix_filter->AddMixedInput(group.get());
}
if (!SetGroupDeviceIds(mix_pipeline.stream_types, mix_filter.get())) {
return false;
}
loopback_output_group_ = mix_filter.get();
filter_groups_.push_back(std::move(mix_filter));
// Create "linearize" processor group:
const auto linearize_pipeline = config->GetLinearizePipeline();
filter_groups_.push_back(
CreateFilterGroup(loopback_output_group_->GetOutputChannelCount(),
"linearize", linearize_pipeline.pipeline, factory));
output_group_ = filter_groups_.back().get();
output_group_->AddMixedInput(loopback_output_group_);
if (!SetGroupDeviceIds(linearize_pipeline.stream_types, output_group_)) {
return false;
}
// If no default group is provided, use the "mix" group.
if (stream_sinks_.find(::media::AudioDeviceDescription::kDefaultDeviceId) ==
stream_sinks_.end()) {
stream_sinks_[::media::AudioDeviceDescription::kDefaultDeviceId] =
loopback_output_group_;
}
return true;
}
bool MixerPipeline::SetGroupDeviceIds(const base::Value* ids,
FilterGroup* filter_group) {
if (!ids) {
return true;
}
DCHECK(filter_group);
DCHECK(ids->is_list());
for (const base::Value& stream_type_val : ids->GetList()) {
DCHECK(stream_type_val.is_string());
const std::string& stream_type = stream_type_val.GetString();
if (!IsOutputDeviceId(stream_type)) {
LOG(ERROR) << stream_type
<< " is not a stream type. Stream types are listed "
<< "in chromecast/media/base/audio_device_ids.cc and "
<< "media/audio/audio_device_description.cc";
return false;
}
if (stream_sinks_.find(stream_type) != stream_sinks_.end()) {
LOG(ERROR) << "Multiple instances of stream type '" << stream_type
<< "' in cast_audio.json";
return false;
}
stream_sinks_[stream_type] = filter_group;
filter_group->AddStreamType(stream_type);
}
return true;
}
void MixerPipeline::Initialize(int output_samples_per_second,
int frames_per_write) {
// The output group will recursively set the sample rate of all other
// FilterGroups.
output_group_->Initialize(output_samples_per_second, frames_per_write);
output_group_->PrintTopology();
}
FilterGroup* MixerPipeline::GetInputGroup(const std::string& device_id) {
auto got = stream_sinks_.find(device_id);
if (got != stream_sinks_.end()) {
return got->second;
}
return stream_sinks_[::media::AudioDeviceDescription::kDefaultDeviceId];
}
void MixerPipeline::MixAndFilter(
int frames_per_write,
MediaPipelineBackend::AudioDecoder::RenderingDelay rendering_delay) {
output_group_->MixAndFilter(frames_per_write, rendering_delay);
}
float* MixerPipeline::GetLoopbackOutput() {
return loopback_output_group_->GetOutputBuffer();
}
float* MixerPipeline::GetOutput() {
return output_group_->GetOutputBuffer();
}
int MixerPipeline::GetLoopbackChannelCount() const {
return loopback_output_group_->GetOutputChannelCount();
}
int MixerPipeline::GetOutputChannelCount() const {
return output_group_->GetOutputChannelCount();
}
int64_t MixerPipeline::GetPostLoopbackRenderingDelayMicroseconds() const {
return output_group_->GetRenderingDelayMicroseconds();
}
void MixerPipeline::SetPostProcessorConfig(const std::string& name,
const std::string& config) {
for (auto&& filter_group : filter_groups_) {
filter_group->SetPostProcessorConfig(name, config);
}
}
void MixerPipeline::SetPlayoutChannel(int playout_channel) {
for (auto& filter_group : filter_groups_) {
filter_group->UpdatePlayoutChannel(playout_channel);
}
}
} // namespace media
} // namespace chromecast