// 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/audio/mixer_service/control_connection.h"
#include <utility>
#include "chromecast/media/audio/mixer_service/mixer_service_transport.pb.h"
#include "chromecast/media/audio/net/common.pb.h"
#include "chromecast/media/audio/net/conversions.h"
#include "net/socket/stream_socket.h"
namespace chromecast {
namespace media {
namespace mixer_service {
ControlConnection::ControlConnection() = default;
ControlConnection::~ControlConnection() = default;
void ControlConnection::Connect(ConnectedCallback callback) {
connect_callback_ = std::move(callback);
MixerConnection::Connect();
}
void ControlConnection::SetVolume(AudioContentType type,
float volume_multiplier) {
if (type == AudioContentType::kOther) {
return;
}
volume_[type] = volume_multiplier;
if (socket_) {
Generic message;
auto* volume = message.mutable_set_device_volume();
volume->set_content_type(audio_service::ConvertContentType(type));
volume->set_volume_multiplier(volume_multiplier);
if (!socket_->SendProto(0, message)) {
OnSendFailed();
}
}
}
void ControlConnection::SetMuted(AudioContentType type, bool muted) {
if (type == AudioContentType::kOther) {
return;
}
muted_[type] = muted;
if (socket_) {
Generic message;
auto* mute_message = message.mutable_set_device_muted();
mute_message->set_content_type(audio_service::ConvertContentType(type));
mute_message->set_muted(muted);
if (!socket_->SendProto(0, message)) {
OnSendFailed();
}
}
}
void ControlConnection::SetVolumeLimit(AudioContentType type,
float max_volume_multiplier) {
if (type == AudioContentType::kOther) {
return;
}
volume_limit_[type] = max_volume_multiplier;
if (socket_) {
Generic message;
auto* limit = message.mutable_set_volume_limit();
limit->set_content_type(audio_service::ConvertContentType(type));
limit->set_max_volume_multiplier(max_volume_multiplier);
if (!socket_->SendProto(0, message)) {
OnSendFailed();
}
}
}
void ControlConnection::ListPostprocessors(
ListPostprocessorsCallback callback) {
list_postprocessors_callbacks_.push_back(std::move(callback));
if (!socket_) {
return;
}
Generic message;
message.mutable_list_postprocessors();
if (!socket_->SendProto(0, message)) {
OnSendFailed();
}
}
void ControlConnection::ConfigurePostprocessor(std::string postprocessor_name,
std::string config) {
postprocessor_config_.insert_or_assign(postprocessor_name, config);
if (!SendPostprocessorMessageInternal(std::move(postprocessor_name),
std::move(config))) {
OnSendFailed();
}
}
void ControlConnection::SendPostprocessorMessage(std::string postprocessor_name,
std::string message) {
SendPostprocessorMessageInternal(std::move(postprocessor_name),
std::move(message));
}
bool ControlConnection::SendPostprocessorMessageInternal(
std::string postprocessor_name,
std::string message) {
if (!socket_) {
return true;
}
// Erase any ? and subsequent substring from the name.
auto q = postprocessor_name.find('?');
if (q != std::string::npos) {
postprocessor_name.erase(q);
}
Generic proto;
auto* content = proto.mutable_configure_postprocessor();
content->set_name(std::move(postprocessor_name));
content->set_config(std::move(message));
return socket_->SendProto(0, proto);
}
void ControlConnection::ReloadPostprocessors() {
if (!socket_) {
return;
}
Generic message;
message.mutable_reload_postprocessors();
socket_->SendProto(0, message);
}
void ControlConnection::SetStreamCountCallback(StreamCountCallback callback) {
stream_count_callback_ = std::move(callback);
if (socket_) {
Generic message;
message.mutable_request_stream_count()->set_subscribe(
!stream_count_callback_.is_null());
if (!socket_->SendProto(0, message)) {
OnSendFailed();
}
}
}
void ControlConnection::SetNumOutputChannels(int num_channels) {
num_output_channels_ = num_channels;
if (socket_) {
Generic message;
message.mutable_set_num_output_channels()->set_channels(num_channels);
if (!socket_->SendProto(0, message)) {
OnSendFailed();
}
}
}
void ControlConnection::OnConnected(std::unique_ptr<MixerSocket> socket) {
socket_ = std::move(socket);
socket_->SetDelegate(this);
for (const auto& item : volume_limit_) {
Generic message;
auto* limit = message.mutable_set_volume_limit();
limit->set_content_type(audio_service::ConvertContentType(item.first));
limit->set_max_volume_multiplier(item.second);
if (!socket_->SendProto(0, message)) {
return OnSendFailed();
}
}
for (const auto& item : muted_) {
Generic message;
auto* muted = message.mutable_set_device_muted();
muted->set_content_type(audio_service::ConvertContentType(item.first));
muted->set_muted(item.second);
if (!socket_->SendProto(0, message)) {
return OnSendFailed();
}
}
for (const auto& item : volume_) {
Generic message;
auto* volume = message.mutable_set_device_volume();
volume->set_content_type(audio_service::ConvertContentType(item.first));
volume->set_volume_multiplier(item.second);
if (!socket_->SendProto(0, message)) {
return OnSendFailed();
}
}
if (stream_count_callback_) {
Generic message;
message.mutable_request_stream_count()->set_subscribe(true);
if (!socket_->SendProto(0, message)) {
return OnSendFailed();
}
}
if (num_output_channels_) {
Generic message;
message.mutable_set_num_output_channels()->set_channels(
num_output_channels_);
if (!socket_->SendProto(0, message)) {
return OnSendFailed();
}
}
for (const auto& item : postprocessor_config_) {
if (!SendPostprocessorMessageInternal(item.first, item.second)) {
return OnSendFailed();
}
}
if (!list_postprocessors_callbacks_.empty()) {
Generic message;
message.mutable_list_postprocessors();
if (!socket_->SendProto(0, message)) {
return OnSendFailed();
}
}
if (connect_callback_) {
connect_callback_.Run();
}
}
void ControlConnection::OnSendFailed() {
LOG(WARNING) << "Failed to send a control message";
OnConnectionError();
}
void ControlConnection::OnConnectionError() {
socket_.reset();
MixerConnection::Connect();
}
bool ControlConnection::HandleMetadata(const Generic& message) {
if (stream_count_callback_ && message.has_stream_count()) {
stream_count_callback_.Run(message.stream_count().primary(),
message.stream_count().sfx());
}
if (message.has_postprocessor_list()) {
std::vector<std::string> post_processors;
for (const auto& post_processor :
message.postprocessor_list().postprocessors()) {
post_processors.push_back(post_processor);
}
while (!list_postprocessors_callbacks_.empty()) {
std::move(list_postprocessors_callbacks_.front()).Run(post_processors);
list_postprocessors_callbacks_.pop_front();
}
}
return true;
}
} // namespace mixer_service
} // namespace media
} // namespace chromecast