Commit d640f605 authored by Ken MacKay's avatar Ken MacKay Committed by Commit Bot

[Chromecast] Use mixer service for all postprocessor control

Remove the shlib APIs. Create a threadsafe helper class for
commonly-used methods.

Merge-With: eureka-internal/327818
Merge-With: eureka-internal/328657
Bug: internal b/138938216
Change-Id: Iab6635043af2dd85057ceafc18cb2f64269651c9
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1877601
Commit-Queue: Kenneth MacKay <kmackay@chromium.org>
Reviewed-by: default avatarYuchen Liu <yucliu@chromium.org>
Cr-Commit-Position: refs/heads/master@{#710224}
parent f6f72088
......@@ -6,6 +6,12 @@ import("//build/buildflag_header.gni")
import("//chromecast/chromecast.gni")
import("//third_party/protobuf/proto_library.gni")
buildflag_header("buildflags") {
header = "buildflags.h"
flags = [ "HAVE_FULL_MIXER=$have_full_mixer" ]
}
proto_library("proto") {
proto_out_dir = "chromecast/media/audio/mixer_service"
sources = [
......@@ -15,6 +21,7 @@ proto_library("proto") {
cast_source_set("common") {
sources = [
"constants.cc",
"constants.h",
"conversions.cc",
"conversions.h",
......@@ -23,6 +30,7 @@ cast_source_set("common") {
]
deps = [
":buildflags",
"//base",
"//chromecast/net:io_buffer_pool",
"//chromecast/public",
......@@ -82,6 +90,20 @@ cast_source_set("control_connection") {
]
}
cast_source_set("mixer_control") {
sources = [
"mixer_control.cc",
"mixer_control.h",
]
deps = [
":common",
":control_connection",
"//base",
"//chromecast/media/audio:audio_io_thread",
]
}
cast_source_set("loopback_connection") {
sources = [
"loopback_connection.cc",
......
......@@ -2,15 +2,16 @@
// 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/receiver/receiver_creation.h"
#include "chromecast/media/audio/mixer_service/constants.h"
#include "chromecast/media/audio/mixer_service/buildflags.h"
namespace chromecast {
namespace media {
namespace mixer_service {
std::unique_ptr<ReceiverInstance> CreateCmaReceiverIfNeeded(
MediaPipelineBackendManager* backend_manager) {
return nullptr;
bool HaveFullMixer() {
return BUILDFLAG(HAVE_FULL_MIXER);
}
} // namespace mixer_service
......
......@@ -20,6 +20,9 @@ enum class MessageType : int16_t {
kAudio,
};
// Returns true if the full mixer is present on the system, false otherwise.
bool HaveFullMixer();
} // namespace mixer_service
} // namespace media
} // namespace chromecast
......
......@@ -71,17 +71,30 @@ void ControlConnection::SetVolumeLimit(AudioContentType type,
}
}
void ControlConnection::ConfigurePostprocessor(const std::string& name,
const void* config,
int size_bytes) {
void ControlConnection::ConfigurePostprocessor(std::string postprocessor_name,
std::string config) {
SendPostprocessorMessage(postprocessor_name, config);
postprocessor_config_.insert_or_assign(std::move(postprocessor_name),
std::move(config));
}
void ControlConnection::SendPostprocessorMessage(std::string postprocessor_name,
std::string message) {
if (!socket_) {
return;
}
Generic message;
auto* content = message.mutable_configure_postprocessor();
content->set_name(name);
content->set_config(static_cast<const char*>(config), size_bytes);
socket_->SendProto(message);
// 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));
socket_->SendProto(proto);
}
void ControlConnection::ReloadPostprocessors() {
......@@ -102,6 +115,15 @@ void ControlConnection::SetStreamCountCallback(StreamCountCallback callback) {
}
}
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);
socket_->SendProto(message);
}
}
void ControlConnection::OnConnected(std::unique_ptr<MixerSocket> socket) {
socket_ = std::move(socket);
socket_->SetDelegate(this);
......@@ -136,6 +158,17 @@ void ControlConnection::OnConnected(std::unique_ptr<MixerSocket> socket) {
socket_->SendProto(message);
}
if (num_output_channels_) {
Generic message;
message.mutable_set_num_output_channels()->set_channels(
num_output_channels_);
socket_->SendProto(message);
}
for (const auto& item : postprocessor_config_) {
SendPostprocessorMessage(item.first, item.second);
}
if (connect_callback_) {
connect_callback_.Run();
}
......
......@@ -37,7 +37,7 @@ class ControlConnection : public MixerConnection, public MixerSocket::Delegate {
// Connects to the mixer. If the mixer connection is lost, this will
// automatically reconnect. If |callback| is nonempty, it will be called each
// time a connection is (re)established with the mixer. This can be used to
// re-send preprocessor configuration, since it is not persisted across
// re-send postprocessor messages, since they are not persisted across
// disconnects.
void Connect(ConnectedCallback callback = ConnectedCallback());
......@@ -50,13 +50,19 @@ class ControlConnection : public MixerConnection, public MixerSocket::Delegate {
// Sets the maximum effective volume multiplier for a given content type.
void SetVolumeLimit(AudioContentType type, float max_volume_multiplier);
// Sends arbitrary config data to a specific postprocessor. Note that the
// config is not persisted across disconnects, and is not saved if
// ConfigurePostprocessor() is called when not connected to the mixer, so
// use the Connect() callback to determine when to (re)send config, if needed.
void ConfigurePostprocessor(const std::string& name,
const void* config,
int size_bytes);
// Sends arbitrary config data to a specific postprocessor. Config is saved
// for each unique |name| and will be resent if the mixer disconnects and then
// reconnects. If the |postprocessor_name| contains a '?', that character and
// the remainder of the name string will not be sent to the mixer; this is
// useful for configuring multiple subprocessors (eg for the dynamic range
// processor).
void ConfigurePostprocessor(std::string postprocessor_name,
std::string config);
// Sends a message a specific postprocessor. Messages are not saved and will
// not be resent if the mixer disconnects and then reconnects.
void SendPostprocessorMessage(std::string postprocessor_name,
std::string message);
// Instructs the mixer to reload postprocessors based on the config file.
void ReloadPostprocessors();
......@@ -65,6 +71,12 @@ class ControlConnection : public MixerConnection, public MixerSocket::Delegate {
// empty callback to remove it.
void SetStreamCountCallback(StreamCountCallback callback);
// Sets the desired number of output channels used by the mixer. This will
// cause an audio interruption on any currently active streams. The actual
// output channel count is determined by the output implementation and may not
// match |num_channels|.
void SetNumOutputChannels(int num_channels);
private:
// MixerConnection implementation:
void OnConnected(std::unique_ptr<MixerSocket> socket) override;
......@@ -81,7 +93,10 @@ class ControlConnection : public MixerConnection, public MixerSocket::Delegate {
base::flat_map<AudioContentType, bool> muted_;
base::flat_map<AudioContentType, float> volume_limit_;
base::flat_map<std::string, std::string> postprocessor_config_;
StreamCountCallback stream_count_callback_;
int num_output_channels_ = 0;
DISALLOW_COPY_AND_ASSIGN(ControlConnection);
};
......
......@@ -21,9 +21,9 @@ media::SampleFormat ConvertSampleFormat(SampleFormat format) {
case SAMPLE_FORMAT_INT16_P:
return kSampleFormatPlanarS16;
case SAMPLE_FORMAT_INT32_P:
return kSampleFormatPlanarF32;
case SAMPLE_FORMAT_FLOAT_P:
return kSampleFormatPlanarS32;
case SAMPLE_FORMAT_FLOAT_P:
return kSampleFormatPlanarF32;
default:
NOTREACHED() << "Unknown sample format " << format;
}
......@@ -40,9 +40,9 @@ SampleFormat ConvertSampleFormat(media::SampleFormat format) {
return SAMPLE_FORMAT_FLOAT_I;
case kSampleFormatPlanarS16:
return SAMPLE_FORMAT_INT16_P;
case kSampleFormatPlanarF32:
return SAMPLE_FORMAT_INT32_P;
case kSampleFormatPlanarS32:
return SAMPLE_FORMAT_INT32_P;
case kSampleFormatPlanarF32:
return SAMPLE_FORMAT_FLOAT_P;
default:
NOTREACHED() << "Unhandled sample format " << format;
......
// Copyright 2019 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/audio/mixer_service/mixer_control.h"
#include <utility>
#include "base/location.h"
#include "base/logging.h"
#include "base/no_destructor.h"
#include "chromecast/media/audio/audio_io_thread.h"
#include "chromecast/media/audio/mixer_service/constants.h"
#include "chromecast/media/audio/mixer_service/control_connection.h"
namespace chromecast {
namespace media {
namespace mixer_service {
// static
MixerControl* MixerControl::Get() {
if (HaveFullMixer()) {
static base::NoDestructor<MixerControl> instance;
return instance.get();
}
return nullptr;
}
MixerControl::MixerControl() : control_(AudioIoThread::Get()->task_runner()) {
DCHECK(HaveFullMixer());
control_.Post(FROM_HERE, &ControlConnection::Connect,
ControlConnection::ConnectedCallback());
}
MixerControl::~MixerControl() = default;
void MixerControl::ConfigurePostprocessor(std::string postprocessor_name,
std::string config) {
control_.Post(FROM_HERE, &ControlConnection::ConfigurePostprocessor,
std::move(postprocessor_name), std::move(config));
}
void MixerControl::ReloadPostprocessors() {
control_.Post(FROM_HERE, &ControlConnection::ReloadPostprocessors);
}
void MixerControl::SetNumOutputChannels(int num_channels) {
control_.Post(FROM_HERE, &ControlConnection::SetNumOutputChannels,
num_channels);
}
} // namespace mixer_service
} // namespace media
} // namespace chromecast
......@@ -2,33 +2,51 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROMECAST_MEDIA_CMA_BACKEND_MIXER_MIXER_CONTROL_H_
#define CHROMECAST_MEDIA_CMA_BACKEND_MIXER_MIXER_CONTROL_H_
#ifndef CHROMECAST_MEDIA_AUDIO_MIXER_SERVICE_MIXER_CONTROL_H_
#define CHROMECAST_MEDIA_AUDIO_MIXER_SERVICE_MIXER_CONTROL_H_
#include "chromecast/public/chromecast_export.h"
#include <string>
#include "base/macros.h"
#include "base/threading/sequence_bound.h"
namespace chromecast {
namespace media {
namespace mixer_service {
class ControlConnection;
// Interface for external control of mixer. The Get() method is only implemented
// when the mixer is actually present.
class CHROMECAST_EXPORT MixerControl {
// Threadsafe process-wide mixer control.
class MixerControl {
public:
// If implemented, returns the control for the current mixer instance. The
// returned pointer is valid until process shutdown.
static MixerControl* Get() __attribute__((__weak__));
// Returns the mixer control instance for this process, or nullptr if the
// mixer is not present on this system.
static MixerControl* Get();
// Sends arbitrary config data to a specific postprocessor.
void ConfigurePostprocessor(std::string postprocessor_name,
std::string config);
// Instructs the mixer to reload postprocessors based on the config file.
void ReloadPostprocessors();
// Sets the desired number of output channels used by the mixer. This will
// cause an audio interruption on any currently active streams. The actual
// output channel count is determined by the output implementation and may not
// match |num_channels|.
virtual void SetNumOutputChannels(int num_channels) = 0;
void SetNumOutputChannels(int num_channels);
private:
friend class base::NoDestructor<MixerControl>;
MixerControl();
~MixerControl();
base::SequenceBound<ControlConnection> control_;
protected:
virtual ~MixerControl() = default;
DISALLOW_COPY_AND_ASSIGN(MixerControl);
};
} // namespace mixer_service
} // namespace media
} // namespace chromecast
#endif // CHROMECAST_MEDIA_CMA_BACKEND_MIXER_MIXER_CONTROL_H_
#endif // CHROMECAST_MEDIA_AUDIO_MIXER_SERVICE_MIXER_CONTROL_H_
......@@ -167,6 +167,11 @@ message StreamCount {
optional int32 sfx = 2;
}
// Sets the desired number of output channels used by the mixer.
message NumOutputChannels {
optional int32 channels = 1;
}
// Indicates an error on an audio stream.
message Error {
enum Type {
......@@ -198,4 +203,5 @@ message Generic {
optional RequestStreamCount request_stream_count = 18;
optional StreamCount stream_count = 19;
optional Error error = 20;
optional NumOutputChannels set_num_output_channels = 21;
}
......@@ -48,21 +48,15 @@ cast_source_set("receiver_cma") {
cast_source_set("receiver_creation") {
sources = [
"receiver_creation.cc",
"receiver_creation.h",
]
deps = [
":receiver",
]
if (have_full_mixer) {
sources += [ "receiver_creation_none.cc" ]
} else {
sources += [ "receiver_creation_cma.cc" ]
deps += [
":receiver_cma",
"//base",
"//chromecast/media/audio:audio_io_thread",
"//chromecast/media/audio/mixer_service:buildflags",
"//chromecast/media/audio/mixer_service:common",
]
}
}
......@@ -107,7 +107,8 @@ class Receiver::InitialSocket : public MixerSocket::Delegate {
message.has_set_volume_limit() ||
message.has_configure_postprocessor() ||
message.has_reload_postprocessors() ||
message.has_request_stream_count()) {
message.has_request_stream_count() ||
message.has_set_num_output_channels()) {
receiver_->CreateControlConnection(std::move(socket_), message);
receiver_->RemoveInitialSocket(this);
}
......
......@@ -6,6 +6,7 @@
#include "base/threading/sequence_bound.h"
#include "chromecast/media/audio/audio_io_thread.h"
#include "chromecast/media/audio/mixer_service/constants.h"
#include "chromecast/media/audio/mixer_service/receiver/receiver_cma.h"
namespace chromecast {
......@@ -29,6 +30,9 @@ class CmaReceiverInstance : public ReceiverInstance {
std::unique_ptr<ReceiverInstance> CreateCmaReceiverIfNeeded(
MediaPipelineBackendManager* backend_manager) {
if (HaveFullMixer()) {
return nullptr;
}
return std::make_unique<CmaReceiverInstance>(backend_manager);
}
......
......@@ -7,8 +7,6 @@
#include <memory>
#include "chromecast/media/audio/mixer_service/receiver/receiver.h"
namespace chromecast {
namespace media {
class MediaPipelineBackendManager;
......
......@@ -203,7 +203,6 @@ cast_source_set("for_mixer_audio") {
sources = [
"audio_decoder_for_mixer.cc",
"audio_decoder_for_mixer.h",
"cast_media_shlib_mixer_audio.cc",
"media_pipeline_backend_for_mixer.cc",
"media_pipeline_backend_for_mixer.h",
"volume_control.cc",
......
// 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.
// Provides CastMediaShlib functions common to all devices using StreamMixer.
#include "chromecast/public/cast_media_shlib.h"
#include <string>
#include <utility>
#include "chromecast/media/cma/backend/mixer/stream_mixer.h"
namespace chromecast {
namespace media {
void CastMediaShlib::ResetPostProcessors(CastMediaShlib::ResultCallback cb) {
StreamMixer::Get()->ResetPostProcessors(std::move(cb));
}
void CastMediaShlib::SetPostProcessorConfig(const std::string& name,
const std::string& config) {
StreamMixer::Get()->SetPostProcessorConfig(name, config);
}
} // namespace media
} // namespace chromecast
......@@ -10,15 +10,6 @@ declare_args() {
mixer_output = ""
}
cast_source_set("mixer_control") {
sources = [
"mixer_control.h",
]
deps = [
"//chromecast/public",
]
}
cast_source_set("post_processor_paths") {
sources = [
"post_processor_paths.cc",
......@@ -77,7 +68,6 @@ cast_source_set("mixer") {
deps = [
":loopback",
":mixer_control",
":post_processor_paths",
"//base",
"//chromecast/base",
......
......@@ -79,6 +79,10 @@ class MixerServiceReceiver::ControlConnection
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;
}
......
......@@ -160,11 +160,6 @@ StreamMixer* StreamMixer::Get() {
return mixer_instance.get();
}
// static
MixerControl* MixerControl::Get() {
return StreamMixer::Get();
}
StreamMixer::StreamMixer()
: StreamMixer(nullptr,
std::make_unique<base::Thread>("CMA mixer"),
......
......@@ -22,7 +22,6 @@
#include "base/threading/sequence_bound.h"
#include "base/threading/thread.h"
#include "base/time/time.h"
#include "chromecast/media/cma/backend/mixer/mixer_control.h"
#include "chromecast/media/cma/backend/mixer/mixer_input.h"
#include "chromecast/media/cma/backend/mixer/mixer_pipeline.h"
#include "chromecast/public/cast_media_shlib.h"
......@@ -64,7 +63,7 @@ class PostProcessingPipelineFactory;
// input sources, then the output sample rate is updated to match the input
// sample rate of the new source.
// * Otherwise, the output sample rate remains unchanged.
class StreamMixer : public MixerControl {
class StreamMixer {
public:
// Returns the mixer instance for this process. Caller must not delete the
// returned instance!
......@@ -72,7 +71,7 @@ class StreamMixer : public MixerControl {
StreamMixer();
// Only public to allow tests to create/destroy mixers.
~StreamMixer() override;
~StreamMixer();
int num_output_channels() const { return num_output_channels_; }
......@@ -116,6 +115,10 @@ class StreamMixer : public MixerControl {
// connections.
void UpdateStreamCounts();
// Sets the desired number of output channels that the mixer should use. The
// actual number of output channels may differ from this value.
void SetNumOutputChannels(int num_channels);
// Test-only methods.
StreamMixer(std::unique_ptr<MixerOutputStream> output,
std::unique_ptr<base::Thread> mixer_thread,
......@@ -150,9 +153,6 @@ class StreamMixer : public MixerControl {
bool muted = false;
};
// MixerControl implementation:
void SetNumOutputChannels(int num_channels) override;
void SetNumOutputChannelsOnThread(int num_channels);
void ResetPostProcessorsOnThread(CastMediaShlib::ResultCallback callback,
const std::string& override_config);
......
......@@ -79,17 +79,6 @@ class CHROMECAST_EXPORT CastMediaShlib {
// Tests if the implementation supports renderer clock rate adjustments.
static bool SupportsMediaClockRateChange();
// Reset the post processing pipeline. |callback| will be called with
// |success| = |true| if the new config loads without error.
static void ResetPostProcessors(ResultCallback callback)
__attribute__((__weak__));
// Updates all postprocessors with the given |name| to have new configuration
// |config|.
static void SetPostProcessorConfig(const std::string& name,
const std::string& config)
__attribute__((__weak__));
// Sets up a direct audio source for output. The media backend will pull audio
// directly from |source| whenever more output data is needed; this provides
// low-latency output. The source must remain valid until
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment