blob: 64defe631a361e7a0d7b29d564f9b5b3adc2ca8a [file] [log] [blame]
// Copyright 2017 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/post_processing_pipeline_impl.h"
#include <cmath>
#include <string>
#include "base/files/file_path.h"
#include "base/json/json_writer.h"
#include "base/memory/ptr_util.h"
#include "base/scoped_native_library.h"
#include "base/values.h"
#include "chromecast/public/media/audio_post_processor2_shlib.h"
#include "chromecast/public/volume_control.h"
namespace chromecast {
namespace media {
namespace {
const int kNoSampleRate = -1;
// Used for AudioPostProcessor(1)
const char kJsonKeyProcessor[] = "processor";
// Used for AudioPostProcessor2
const char kJsonKeyLib[] = "lib";
const char kJsonKeyName[] = "name";
const char kJsonKeyConfig[] = "config";
} // namespace
PostProcessingPipelineFactoryImpl::PostProcessingPipelineFactoryImpl() =
default;
PostProcessingPipelineFactoryImpl::~PostProcessingPipelineFactoryImpl() =
default;
std::unique_ptr<PostProcessingPipeline>
PostProcessingPipelineFactoryImpl::CreatePipeline(
const std::string& name,
const base::ListValue* filter_description_list,
int num_channels) {
return std::make_unique<PostProcessingPipelineImpl>(
name, filter_description_list, num_channels);
}
PostProcessingPipelineImpl::PostProcessingPipelineImpl(
const std::string& name,
const base::ListValue* filter_description_list,
int channels)
: name_(name), sample_rate_(kNoSampleRate), num_output_channels_(channels) {
if (!filter_description_list) {
return; // Warning logged.
}
for (const base::Value& processor_description_dict :
filter_description_list->GetList()) {
DCHECK(processor_description_dict.is_dict());
std::string processor_name;
const base::Value* name_val = processor_description_dict.FindKeyOfType(
kJsonKeyName, base::Value::Type::STRING);
if (name_val) {
processor_name = name_val->GetString();
}
if (!processor_name.empty()) {
std::vector<PostProcessorInfo>::iterator it =
find_if(processors_.begin(), processors_.end(),
[&processor_name](PostProcessorInfo& p) {
return p.name == processor_name;
});
LOG_IF(DFATAL, it != processors_.end())
<< "Duplicate postprocessor name " << processor_name;
}
std::string library_path;
// Keys for AudioPostProcessor2:
const base::Value* library_val = processor_description_dict.FindKeyOfType(
kJsonKeyLib, base::Value::Type::STRING);
if (library_val) {
library_path = library_val->GetString();
} else {
// Keys for AudioPostProcessor
// TODO(bshaya): Remove when AudioPostProcessor support is removed.
library_val = processor_description_dict.FindKeyOfType(
kJsonKeyProcessor, base::Value::Type::STRING);
DCHECK(library_val) << "Post processor description is missing key "
<< kJsonKeyLib;
library_path = library_val->GetString();
}
std::string processor_config_string;
const base::Value* processor_config_val =
processor_description_dict.FindKey(kJsonKeyConfig);
if (processor_config_val) {
DCHECK(processor_config_val->is_dict() ||
processor_config_val->is_string());
base::JSONWriter::Write(*processor_config_val, &processor_config_string);
}
LOG(INFO) << "Creating an instance of " << library_path << "("
<< processor_config_string << ")";
processors_.emplace_back(
PostProcessorInfo{factory_.CreatePostProcessor(
library_path, processor_config_string, channels),
processor_name});
channels = processors_.back().ptr->NumOutputChannels();
}
num_output_channels_ = channels;
}
PostProcessingPipelineImpl::~PostProcessingPipelineImpl() = default;
int PostProcessingPipelineImpl::ProcessFrames(float* data,
int num_frames,
float current_multiplier,
bool is_silence) {
DCHECK_NE(sample_rate_, kNoSampleRate);
DCHECK(data);
output_buffer_ = data;
if (is_silence) {
if (!IsRinging()) {
return total_delay_frames_; // Output will be silence.
}
silence_frames_processed_ += num_frames;
} else {
silence_frames_processed_ = 0;
}
UpdateCastVolume(current_multiplier);
total_delay_frames_ = 0;
for (auto& processor : processors_) {
total_delay_frames_ += processor.ptr->ProcessFrames(
output_buffer_, num_frames, cast_volume_, current_dbfs_);
output_buffer_ = processor.ptr->GetOutputBuffer();
}
return total_delay_frames_;
}
int PostProcessingPipelineImpl::NumOutputChannels() {
return num_output_channels_;
}
float* PostProcessingPipelineImpl::GetOutputBuffer() {
DCHECK(output_buffer_);
return output_buffer_;
}
bool PostProcessingPipelineImpl::SetSampleRate(int sample_rate) {
sample_rate_ = sample_rate;
bool result = true;
for (auto& processor : processors_) {
result &= processor.ptr->SetSampleRate(sample_rate_);
}
ringing_time_in_frames_ = GetRingingTimeInFrames();
silence_frames_processed_ = 0;
return result;
}
bool PostProcessingPipelineImpl::IsRinging() {
return silence_frames_processed_ < ringing_time_in_frames_;
}
int PostProcessingPipelineImpl::GetRingingTimeInFrames() {
int memory_frames = 0;
for (auto& processor : processors_) {
memory_frames += processor.ptr->GetRingingTimeInFrames();
}
return memory_frames;
}
void PostProcessingPipelineImpl::UpdateCastVolume(float multiplier) {
DCHECK_GE(multiplier, 0.0);
if (multiplier == current_multiplier_) {
return;
}
current_multiplier_ = multiplier;
current_dbfs_ = (multiplier == 0.0f ? -200.0f : std::log10(multiplier) * 20);
DCHECK(chromecast::media::VolumeControl::DbFSToVolume);
cast_volume_ = chromecast::media::VolumeControl::DbFSToVolume(current_dbfs_);
}
// Send string |config| to postprocessor |name|.
void PostProcessingPipelineImpl::SetPostProcessorConfig(
const std::string& name,
const std::string& config) {
DCHECK(!name.empty());
std::vector<PostProcessorInfo>::iterator it =
find_if(processors_.begin(), processors_.end(),
[&name](PostProcessorInfo& p) { return p.name == name; });
if (it != processors_.end()) {
it->ptr->UpdateParameters(config);
VLOG(1) << "Config string: " << config << " was delivered to postprocessor "
<< name;
}
}
// Set content type.
void PostProcessingPipelineImpl::SetContentType(AudioContentType content_type) {
for (auto& processor : processors_) {
processor.ptr->SetContentType(content_type);
}
}
void PostProcessingPipelineImpl::UpdatePlayoutChannel(int channel) {
for (auto& processor : processors_) {
processor.ptr->SetPlayoutChannel(channel);
}
}
} // namespace media
} // namespace chromecast