// Copyright 2019 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/mixer_service_receiver.h"
#include <string>
#include <utility>
#include "base/logging.h"
#include "chromecast/media/audio/mixer_service/mixer_service_transport.pb.h"
#include "chromecast/media/audio/mixer_service/mixer_socket.h"
#include "chromecast/media/audio/net/common.pb.h"
#include "chromecast/media/audio/net/conversions.h"
#include "chromecast/media/cma/backend/mixer/audio_output_redirector.h"
#include "chromecast/media/cma/backend/mixer/loopback_handler.h"
#include "chromecast/media/cma/backend/mixer/mixer_input_connection.h"
#include "chromecast/media/cma/backend/mixer/mixer_loopback_connection.h"
#include "chromecast/media/cma/backend/mixer/post_processor_registry.h"
#include "chromecast/media/cma/backend/mixer/stream_mixer.h"
namespace chromecast {
namespace media {
namespace {
enum MessageTypes : int {
kStreamCounts = 1,
kPostProcessorList,
};
} // namespace
class MixerServiceReceiver::ControlConnection
: public mixer_service::MixerSocket::Delegate {
public:
ControlConnection(StreamMixer* mixer,
MixerServiceReceiver* receiver,
std::unique_ptr<mixer_service::MixerSocket> socket)
: mixer_(mixer), receiver_(receiver), socket_(std::move(socket)) {
DCHECK(mixer_);
DCHECK(receiver_);
DCHECK(socket_);
socket_->SetDelegate(this);
}
ControlConnection(const ControlConnection&) = delete;
ControlConnection& operator=(const ControlConnection&) = delete;
~ControlConnection() override = default;
void OnStreamCountChanged() {
if (!send_stream_count_) {
return;
}
mixer_service::Generic message;
auto* counts = message.mutable_stream_count();
counts->set_primary(receiver_->primary_stream_count_);
counts->set_sfx(receiver_->sfx_stream_count_);
socket_->SendProto(kStreamCounts, message);
}
private:
friend class MixerServiceReceiver;
// mixer_service::MixerSocket::Delegate implementation:
bool HandleMetadata(const mixer_service::Generic& message) override {
if (message.has_set_volume_limit()) {
mixer_->SetOutputLimit(
audio_service::ConvertContentType(
message.set_volume_limit().content_type()),
message.set_volume_limit().max_volume_multiplier());
}
if (message.has_set_device_muted()) {
mixer_->SetMuted(audio_service::ConvertContentType(
message.set_device_muted().content_type()),
message.set_device_muted().muted());
}
if (message.has_set_device_volume()) {
mixer_->SetVolume(audio_service::ConvertContentType(
message.set_device_volume().content_type()),
message.set_device_volume().volume_multiplier());
}
if (message.has_list_postprocessors()) {
OnListPostprocessors();
}
if (message.has_configure_postprocessor()) {
mixer_->SetPostProcessorConfig(
message.configure_postprocessor().name(),
message.configure_postprocessor().config());
}
if (message.has_reload_postprocessors()) {
mixer_->ResetPostProcessors([](bool, const std::string&) {});
}
if (message.has_request_stream_count()) {
send_stream_count_ = message.request_stream_count().subscribe();
OnStreamCountChanged();
}
if (message.has_set_num_output_channels()) {
mixer_->SetNumOutputChannels(
message.set_num_output_channels().channels());
}
return true;
}
bool HandleAudioData(char* data, size_t size, int64_t timestamp) override {
return true;
}
bool HandleAudioBuffer(scoped_refptr<net::IOBuffer> buffer,
char* data,
size_t size,
int64_t timestamp) override {
return true;
}
void OnListPostprocessors() {
mixer_service::Generic message;
auto* postprocessor_list = message.mutable_postprocessor_list();
for (const auto& library_pair : PostProcessorRegistry::Get()->Libraries()) {
postprocessor_list->add_postprocessors(library_pair.first);
}
socket_->SendProto(kPostProcessorList, message);
}
void OnConnectionError() override {
receiver_->RemoveControlConnection(this);
}
StreamMixer* const mixer_;
MixerServiceReceiver* const receiver_;
const std::unique_ptr<mixer_service::MixerSocket> socket_;
bool send_stream_count_ = false;
};
MixerServiceReceiver::MixerServiceReceiver(StreamMixer* mixer,
LoopbackHandler* loopback_handler)
: mixer_(mixer), loopback_handler_(loopback_handler) {
DCHECK(mixer_);
DCHECK(loopback_handler_);
}
MixerServiceReceiver::~MixerServiceReceiver() = default;
void MixerServiceReceiver::OnStreamCountChanged(int primary, int sfx) {
primary_stream_count_ = primary;
sfx_stream_count_ = sfx;
for (const auto& control : control_connections_) {
control.second->OnStreamCountChanged();
}
}
void MixerServiceReceiver::CreateOutputStream(
std::unique_ptr<mixer_service::MixerSocket> socket,
const mixer_service::Generic& message) {
DCHECK(message.has_output_stream_params());
// MixerInputConnection manages its own lifetime.
auto* connection = new MixerInputConnection(mixer_, std::move(socket),
message.output_stream_params());
connection->HandleMetadata(message);
}
void MixerServiceReceiver::CreateLoopbackConnection(
std::unique_ptr<mixer_service::MixerSocket> socket,
const mixer_service::Generic& message) {
auto connection =
std::make_unique<MixerLoopbackConnection>(std::move(socket));
loopback_handler_->AddConnection(std::move(connection));
}
void MixerServiceReceiver::CreateAudioRedirection(
std::unique_ptr<mixer_service::MixerSocket> socket,
const mixer_service::Generic& message) {
if (message.redirection_request().has_num_channels() &&
message.redirection_request().num_channels() <= 0) {
LOG(INFO) << "Bad redirection request";
return;
}
mixer_->AddAudioOutputRedirector(std::make_unique<AudioOutputRedirector>(
mixer_, std::move(socket), message));
}
void MixerServiceReceiver::CreateControlConnection(
std::unique_ptr<mixer_service::MixerSocket> socket,
const mixer_service::Generic& message) {
auto connection =
std::make_unique<ControlConnection>(mixer_, this, std::move(socket));
ControlConnection* ptr = connection.get();
control_connections_[ptr] = std::move(connection);
ptr->HandleMetadata(message);
}
void MixerServiceReceiver::RemoveControlConnection(ControlConnection* ptr) {
control_connections_.erase(ptr);
}
} // namespace media
} // namespace chromecast