// Copyright 2017 The Chromium Authors
// 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/post_processing_pipeline_parser.h"
#include <utility>
#include "base/check.h"
#include "base/files/file_util.h"
#include "base/json/json_file_value_serializer.h"
#include "base/logging.h"
#include "chromecast/media/base/audio_device_ids.h"
#include "media/audio/audio_device_description.h"
namespace chromecast {
namespace media {
namespace {
const char kLinearizePipelineKey[] = "linearize";
const char kMixPipelineKey[] = "mix";
const char kNameKey[] = "name";
const char kNumInputChannelsKey[] = "num_input_channels";
const char kOutputStreamsKey[] = "output_streams";
const char kPostProcessorsKey[] = "postprocessors";
const char kProcessorsKey[] = "processors";
const char kRenderNameTag[] = "render";
const char kStreamsKey[] = "streams";
const char kVolumeLimitsKey[] = "volume_limits";
void SplitPipeline(const base::Value::List& processors_list,
base::Value::List& prerender_pipeline,
base::Value::List& postrender_pipeline) {
bool has_render = false;
for (const base::Value& processor_description_value : processors_list) {
DCHECK(processor_description_value.is_dict());
const std::string* name =
processor_description_value.GetDict().FindString(kNameKey);
if (name && *name == kRenderNameTag) {
has_render = true;
break;
}
}
bool is_prerender = has_render;
for (const base::Value& processor_description_dict : processors_list) {
const std::string* name =
processor_description_dict.GetDict().FindString(kNameKey);
if (name && *name == kRenderNameTag) {
is_prerender = false;
continue;
}
if (is_prerender) {
prerender_pipeline.Append(processor_description_dict.Clone());
} else {
postrender_pipeline.Append(processor_description_dict.Clone());
}
}
}
} // namespace
StreamPipelineDescriptor::StreamPipelineDescriptor(
base::Value prerender_pipeline_in,
base::Value pipeline_in,
const base::Value* stream_types_in,
const std::optional<int> num_input_channels_in,
const base::Value* volume_limits_in)
: prerender_pipeline(std::move(prerender_pipeline_in)),
pipeline(std::move(pipeline_in)),
stream_types(stream_types_in),
num_input_channels(std::move(num_input_channels_in)),
volume_limits(volume_limits_in) {}
StreamPipelineDescriptor::~StreamPipelineDescriptor() = default;
StreamPipelineDescriptor::StreamPipelineDescriptor(
StreamPipelineDescriptor&& other) = default;
StreamPipelineDescriptor& StreamPipelineDescriptor::operator=(
StreamPipelineDescriptor&& other) = default;
PostProcessingPipelineParser::PostProcessingPipelineParser(
base::Value config_dict)
: file_path_(""), config_dict_(std::move(config_dict).TakeDict()) {
postprocessor_config_ = config_dict_.FindDict(kPostProcessorsKey);
if (!postprocessor_config_) {
LOG(WARNING) << "No post-processor config found.";
}
}
PostProcessingPipelineParser::PostProcessingPipelineParser(
const base::FilePath& file_path)
: file_path_(file_path) {
if (!base::PathExists(file_path_)) {
LOG(WARNING) << "No post-processing config found at " << file_path_ << ".";
return;
}
JSONFileValueDeserializer deserializer(file_path_);
int error_code = -1;
std::string error_msg;
auto config_dict_ptr = deserializer.Deserialize(&error_code, &error_msg);
CHECK(config_dict_ptr) << "Invalid JSON in " << file_path_ << " error "
<< error_code << ":" << error_msg;
config_dict_ = std::move(config_dict_ptr->GetDict());
postprocessor_config_ = config_dict_.FindDict(kPostProcessorsKey);
if (!postprocessor_config_) {
LOG(WARNING) << "No post-processor config found.";
}
}
PostProcessingPipelineParser::~PostProcessingPipelineParser() = default;
std::vector<StreamPipelineDescriptor>
PostProcessingPipelineParser::GetStreamPipelines() {
std::vector<StreamPipelineDescriptor> descriptors;
if (!postprocessor_config_) {
return descriptors;
}
const base::Value::List* pipelines_list =
postprocessor_config_->FindList(kOutputStreamsKey);
if (!pipelines_list) {
LOG(WARNING) << "No post-processors found for streams (key = "
<< kOutputStreamsKey
<< ").\n No stream-specific processing will occur.";
return descriptors;
}
for (const base::Value& pipeline_description_val : *pipelines_list) {
CHECK(pipeline_description_val.is_dict());
const base::Value::Dict& pipeline_description_dict =
pipeline_description_val.GetDict();
const base::Value::List* processors_list =
pipeline_description_dict.FindList(kProcessorsKey);
CHECK(processors_list);
base::Value::List prerender_pipeline;
base::Value::List postrender_pipeline;
SplitPipeline(*processors_list, prerender_pipeline, postrender_pipeline);
const base::Value* streams_list =
pipeline_description_dict.Find(kStreamsKey);
CHECK(streams_list && streams_list->is_list());
auto num_input_channels =
pipeline_description_dict.FindInt(kNumInputChannelsKey);
const base::Value* volume_limits =
pipeline_description_dict.Find(kVolumeLimitsKey);
CHECK(!volume_limits || volume_limits->is_list());
descriptors.emplace_back(base::Value(std::move(prerender_pipeline)),
base::Value(std::move(postrender_pipeline)),
streams_list, std::move(num_input_channels),
volume_limits);
}
return descriptors;
}
StreamPipelineDescriptor PostProcessingPipelineParser::GetMixPipeline() {
return GetPipelineByKey(kMixPipelineKey);
}
StreamPipelineDescriptor PostProcessingPipelineParser::GetLinearizePipeline() {
return GetPipelineByKey(kLinearizePipelineKey);
}
StreamPipelineDescriptor PostProcessingPipelineParser::GetPipelineByKey(
const std::string& key) {
const base::Value* stream_value =
postprocessor_config_ ? postprocessor_config_->FindByDottedPath(key)
: nullptr;
if (!postprocessor_config_ || !stream_value) {
LOG(WARNING) << "No post-processor description found for \"" << key
<< "\" in " << file_path_ << ". Using passthrough.";
return StreamPipelineDescriptor(base::Value(base::Value::Type::LIST),
base::Value(base::Value::Type::LIST),
nullptr, std::nullopt, nullptr);
}
const base::Value::Dict& stream_dict = stream_value->GetDict();
const base::Value::List* processors_list =
stream_dict.FindList(kProcessorsKey);
CHECK(processors_list);
base::Value::List prerender_pipeline;
base::Value::List postrender_pipeline;
SplitPipeline(*processors_list, prerender_pipeline, postrender_pipeline);
const base::Value* streams_list = stream_dict.Find(kStreamsKey);
if (streams_list && !streams_list->is_list()) {
streams_list = nullptr;
}
const base::Value* volume_limits = stream_dict.Find(kVolumeLimitsKey);
if (volume_limits && !volume_limits->is_dict()) {
volume_limits = nullptr;
}
return StreamPipelineDescriptor(
base::Value(std::move(prerender_pipeline)),
base::Value(std::move(postrender_pipeline)), streams_list,
stream_dict.FindInt(kNumInputChannelsKey), volume_limits);
}
base::FilePath PostProcessingPipelineParser::GetFilePath() const {
return file_path_;
}
} // namespace media
} // namespace chromecast