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

[Chromecast] Add the ability to modify which streams redirection applies to

Allow updating the patterns that the redirector matches to mixer
streams without removing and re-adding the redirector.

Bug: internal b/123626919
Test: cast_audio_backend_unittests
Change-Id: I4f79d9d1c1c95ba213bc1396d9d708fd8bb037bc
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1506670Reviewed-by: default avatarYuchen Liu <yucliu@chromium.org>
Reviewed-by: default avatarLuke Halliwell <halliwell@chromium.org>
Commit-Queue: Kenneth MacKay <kmackay@chromium.org>
Cr-Commit-Position: refs/heads/master@{#638691}
parent a57f4dbb
...@@ -118,11 +118,14 @@ AudioOutputRedirector::~AudioOutputRedirector() = default; ...@@ -118,11 +118,14 @@ AudioOutputRedirector::~AudioOutputRedirector() = default;
void AudioOutputRedirector::AddInput(MixerInput* mixer_input) { void AudioOutputRedirector::AddInput(MixerInput* mixer_input) {
if (ApplyToInput(mixer_input)) { if (ApplyToInput(mixer_input)) {
inputs_[mixer_input] = std::make_unique<InputImpl>(this, mixer_input); inputs_[mixer_input] = std::make_unique<InputImpl>(this, mixer_input);
} else {
non_redirected_inputs_.insert(mixer_input);
} }
} }
void AudioOutputRedirector::RemoveInput(MixerInput* mixer_input) { void AudioOutputRedirector::RemoveInput(MixerInput* mixer_input) {
inputs_.erase(mixer_input); inputs_.erase(mixer_input);
non_redirected_inputs_.erase(mixer_input);
} }
bool AudioOutputRedirector::ApplyToInput(MixerInput* mixer_input) { bool AudioOutputRedirector::ApplyToInput(MixerInput* mixer_input) {
...@@ -140,6 +143,33 @@ bool AudioOutputRedirector::ApplyToInput(MixerInput* mixer_input) { ...@@ -140,6 +143,33 @@ bool AudioOutputRedirector::ApplyToInput(MixerInput* mixer_input) {
return false; return false;
} }
void AudioOutputRedirector::UpdatePatterns(
std::vector<std::pair<AudioContentType, std::string>> patterns) {
config_.stream_match_patterns = std::move(patterns);
// Remove streams that no longer match.
for (auto it = inputs_.begin(); it != inputs_.end();) {
MixerInput* mixer_input = it->first;
if (!ApplyToInput(mixer_input)) {
non_redirected_inputs_.insert(mixer_input);
it = inputs_.erase(it);
} else {
++it;
}
}
// Add streams that previously didn't match.
for (auto it = non_redirected_inputs_.begin();
it != non_redirected_inputs_.end();) {
MixerInput* mixer_input = *it;
if (ApplyToInput(mixer_input)) {
inputs_[mixer_input] = std::make_unique<InputImpl>(this, mixer_input);
it = non_redirected_inputs_.erase(it);
} else {
++it;
}
}
}
void AudioOutputRedirector::Start(int output_samples_per_second) { void AudioOutputRedirector::Start(int output_samples_per_second) {
output_->Start(output_samples_per_second); output_->Start(output_samples_per_second);
} }
...@@ -221,8 +251,7 @@ void AudioOutputRedirector::FinishBuffer() { ...@@ -221,8 +251,7 @@ void AudioOutputRedirector::FinishBuffer() {
AudioOutputRedirectorToken* CastMediaShlib::AddAudioOutputRedirection( AudioOutputRedirectorToken* CastMediaShlib::AddAudioOutputRedirection(
const AudioOutputRedirectionConfig& config, const AudioOutputRedirectionConfig& config,
std::unique_ptr<RedirectedAudioOutput> output) { std::unique_ptr<RedirectedAudioOutput> output) {
if (!output || config.num_output_channels <= 0 || if (!output || config.num_output_channels <= 0) {
config.stream_match_patterns.empty()) {
return nullptr; return nullptr;
} }
...@@ -243,5 +272,18 @@ void CastMediaShlib::RemoveAudioOutputRedirection( ...@@ -243,5 +272,18 @@ void CastMediaShlib::RemoveAudioOutputRedirection(
} }
} }
// static
void CastMediaShlib::ModifyAudioOutputRedirection(
AudioOutputRedirectorToken* token,
std::vector<std::pair<AudioContentType, std::string>>
stream_match_patterns) {
AudioOutputRedirector* redirector =
static_cast<AudioOutputRedirector*>(token);
if (redirector) {
StreamMixer::Get()->ModifyAudioOutputRedirection(
redirector, std::move(stream_match_patterns));
}
}
} // namespace media } // namespace media
} // namespace chromecast } // namespace chromecast
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
#include <cstdint> #include <cstdint>
#include <memory> #include <memory>
#include <string> #include <string>
#include <utility>
#include <vector> #include <vector>
#include "base/containers/flat_map.h" #include "base/containers/flat_map.h"
...@@ -60,6 +61,12 @@ class AudioOutputRedirector : public AudioOutputRedirectorToken { ...@@ -60,6 +61,12 @@ class AudioOutputRedirector : public AudioOutputRedirectorToken {
void AddInput(MixerInput* mixer_input); void AddInput(MixerInput* mixer_input);
void RemoveInput(MixerInput* mixer_input); void RemoveInput(MixerInput* mixer_input);
// Updates the set of patterns used to determine which inputs should be
// redirected by this AudioOutputRedirector. Any inputs which no longer match
// will stop being redirected.
void UpdatePatterns(
std::vector<std::pair<AudioContentType, std::string>> patterns);
// Indicates that mixer output is starting at the given sample rate of // Indicates that mixer output is starting at the given sample rate of
// |output_samples_per_second|. // |output_samples_per_second|.
void Start(int output_samples_per_second); void Start(int output_samples_per_second);
...@@ -88,7 +95,7 @@ class AudioOutputRedirector : public AudioOutputRedirectorToken { ...@@ -88,7 +95,7 @@ class AudioOutputRedirector : public AudioOutputRedirectorToken {
bool ApplyToInput(MixerInput* mixer_input); bool ApplyToInput(MixerInput* mixer_input);
const AudioOutputRedirectionConfig config_; AudioOutputRedirectionConfig config_;
const std::unique_ptr<RedirectedAudioOutput> output_; const std::unique_ptr<RedirectedAudioOutput> output_;
int next_num_frames_ = 0; int next_num_frames_ = 0;
...@@ -99,6 +106,7 @@ class AudioOutputRedirector : public AudioOutputRedirectorToken { ...@@ -99,6 +106,7 @@ class AudioOutputRedirector : public AudioOutputRedirectorToken {
std::vector<float*> channel_data_; std::vector<float*> channel_data_;
base::flat_map<MixerInput*, std::unique_ptr<InputImpl>> inputs_; base::flat_map<MixerInput*, std::unique_ptr<InputImpl>> inputs_;
base::flat_set<MixerInput*> non_redirected_inputs_;
DISALLOW_COPY_AND_ASSIGN(AudioOutputRedirector); DISALLOW_COPY_AND_ASSIGN(AudioOutputRedirector);
}; };
......
...@@ -780,6 +780,24 @@ void StreamMixer::RemoveAudioOutputRedirectorOnThread( ...@@ -780,6 +780,24 @@ void StreamMixer::RemoveAudioOutputRedirectorOnThread(
audio_output_redirectors_.erase(redirector); audio_output_redirectors_.erase(redirector);
} }
void StreamMixer::ModifyAudioOutputRedirection(
AudioOutputRedirector* redirector,
std::vector<std::pair<AudioContentType, std::string>>
stream_match_patterns) {
POST_THROUGH_INPUT_THREAD(&StreamMixer::ModifyAudioOutputRedirectionOnThread,
redirector, std::move(stream_match_patterns));
}
void StreamMixer::ModifyAudioOutputRedirectionOnThread(
AudioOutputRedirector* redirector,
std::vector<std::pair<AudioContentType, std::string>>
stream_match_patterns) {
auto it = audio_output_redirectors_.find(redirector);
if (it != audio_output_redirectors_.end()) {
it->second->UpdatePatterns(std::move(stream_match_patterns));
}
}
void StreamMixer::PostLoopbackData(int64_t expected_playback_time, void StreamMixer::PostLoopbackData(int64_t expected_playback_time,
SampleFormat format, SampleFormat format,
int sample_rate, int sample_rate,
......
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
#include <memory> #include <memory>
#include <string> #include <string>
#include <utility>
#include <vector> #include <vector>
#include "base/containers/flat_map.h" #include "base/containers/flat_map.h"
...@@ -100,6 +101,10 @@ class StreamMixer { ...@@ -100,6 +101,10 @@ class StreamMixer {
void AddAudioOutputRedirector( void AddAudioOutputRedirector(
std::unique_ptr<AudioOutputRedirector> redirector); std::unique_ptr<AudioOutputRedirector> redirector);
void RemoveAudioOutputRedirector(AudioOutputRedirector* redirector); void RemoveAudioOutputRedirector(AudioOutputRedirector* redirector);
void ModifyAudioOutputRedirection(
AudioOutputRedirector* redirector,
std::vector<std::pair<AudioContentType, std::string>>
stream_match_patterns);
// Sets the volume multiplier for the given content |type|. // Sets the volume multiplier for the given content |type|.
void SetVolume(AudioContentType type, float level); void SetVolume(AudioContentType type, float level);
...@@ -194,6 +199,10 @@ class StreamMixer { ...@@ -194,6 +199,10 @@ class StreamMixer {
void AddAudioOutputRedirectorOnThread( void AddAudioOutputRedirectorOnThread(
std::unique_ptr<AudioOutputRedirector> redirector); std::unique_ptr<AudioOutputRedirector> redirector);
void RemoveAudioOutputRedirectorOnThread(AudioOutputRedirector* redirector); void RemoveAudioOutputRedirectorOnThread(AudioOutputRedirector* redirector);
void ModifyAudioOutputRedirectionOnThread(
AudioOutputRedirector* redirector,
std::vector<std::pair<AudioContentType, std::string>>
stream_match_patterns);
void PostLoopbackData(int64_t expected_playback_time, void PostLoopbackData(int64_t expected_playback_time,
SampleFormat sample_format, SampleFormat sample_format,
......
...@@ -385,12 +385,16 @@ class StreamMixerTest : public testing::Test { ...@@ -385,12 +385,16 @@ class StreamMixerTest : public testing::Test {
} }
MockRedirectedAudioOutput* AddOutputRedirector( MockRedirectedAudioOutput* AddOutputRedirector(
const AudioOutputRedirectionConfig& config) { const AudioOutputRedirectionConfig& config,
AudioOutputRedirector** redirector_ptr = nullptr) {
auto redirected_output = auto redirected_output =
std::make_unique<MockRedirectedAudioOutput>(kNumChannels); std::make_unique<MockRedirectedAudioOutput>(kNumChannels);
MockRedirectedAudioOutput* redirected_output_ptr = redirected_output.get(); MockRedirectedAudioOutput* redirected_output_ptr = redirected_output.get();
auto redirector = std::make_unique<AudioOutputRedirector>( auto redirector = std::make_unique<AudioOutputRedirector>(
config, std::move(redirected_output)); config, std::move(redirected_output));
if (redirector_ptr) {
*redirector_ptr = redirector.get();
}
mixer_->AddAudioOutputRedirector(std::move(redirector)); mixer_->AddAudioOutputRedirector(std::move(redirector));
return redirected_output_ptr; return redirected_output_ptr;
} }
...@@ -1332,6 +1336,64 @@ TEST_F(StreamMixerTest, OutputRedirectionNoMatch) { ...@@ -1332,6 +1336,64 @@ TEST_F(StreamMixerTest, OutputRedirectionNoMatch) {
mixer_.reset(); mixer_.reset();
} }
TEST_F(StreamMixerTest, ModifyOutputRedirection) {
std::vector<std::unique_ptr<MockMixerSource>> inputs;
inputs.push_back(
std::make_unique<MockMixerSource>(kTestSamplesPerSecond, "matches"));
inputs.push_back(
std::make_unique<MockMixerSource>(kTestSamplesPerSecond, "asdf"));
for (size_t i = 0; i < inputs.size(); ++i) {
mixer_->AddInput(inputs[i].get());
}
WaitForMixer();
mock_output_->ClearData();
AudioOutputRedirectionConfig config;
config.stream_match_patterns.push_back({AudioContentType::kMedia, "*match*"});
AudioOutputRedirector* redirector_ptr = nullptr;
MockRedirectedAudioOutput* redirected_output_ptr =
AddOutputRedirector(config, &redirector_ptr);
CHECK(redirector_ptr);
WaitForMixer();
const int kNumFrames = 32;
for (size_t i = 0; i < inputs.size(); ++i) {
inputs[i]->SetData(GetTestData(i));
}
PlaybackOnce();
mock_output_->ClearData();
for (size_t i = 0; i < inputs.size(); ++i) {
inputs[i]->SetData(GetTestData(i));
}
PlaybackOnce();
CheckRedirectorOutput(redirected_output_ptr, {inputs[1].get()},
{inputs[0].get()}, kNumFrames);
std::vector<std::pair<AudioContentType, std::string>> new_match_patterns = {
{AudioContentType::kMedia, "*asdf*"}};
mixer_->ModifyAudioOutputRedirection(redirector_ptr,
std::move(new_match_patterns));
WaitForMixer();
mock_output_->ClearData();
for (size_t i = 0; i < inputs.size(); ++i) {
inputs[i]->SetData(GetTestData(i));
}
PlaybackOnce();
mock_output_->ClearData();
for (size_t i = 0; i < inputs.size(); ++i) {
inputs[i]->SetData(GetTestData(i));
}
PlaybackOnce();
CheckRedirectorOutput(redirected_output_ptr, {inputs[0].get()},
{inputs[1].get()}, kNumFrames);
mixer_.reset();
}
#if GTEST_HAS_DEATH_TEST #if GTEST_HAS_DEATH_TEST
using StreamMixerDeathTest = StreamMixerTest; using StreamMixerDeathTest = StreamMixerTest;
......
...@@ -10,9 +10,11 @@ ...@@ -10,9 +10,11 @@
#include <functional> #include <functional>
#include <memory> #include <memory>
#include <string> #include <string>
#include <utility>
#include <vector> #include <vector>
#include "chromecast_export.h" #include "chromecast_export.h"
#include "volume_control.h"
namespace chromecast { namespace chromecast {
namespace media { namespace media {
...@@ -176,6 +178,12 @@ class CHROMECAST_EXPORT CastMediaShlib { ...@@ -176,6 +178,12 @@ class CHROMECAST_EXPORT CastMediaShlib {
// AddAudioOutputRedirection(). // AddAudioOutputRedirection().
static void RemoveAudioOutputRedirection(AudioOutputRedirectorToken* token) static void RemoveAudioOutputRedirection(AudioOutputRedirectorToken* token)
__attribute__((__weak__)); __attribute__((__weak__));
// Updates the set of streams that an audio output redirector should apply to.
static void ModifyAudioOutputRedirection(
AudioOutputRedirectorToken* token,
std::vector<std::pair<AudioContentType, std::string /* device ID */>>
stream_match_patterns) __attribute__((__weak__));
}; };
} // namespace media } // namespace media
......
...@@ -34,7 +34,7 @@ struct AudioOutputRedirectionConfig { ...@@ -34,7 +34,7 @@ struct AudioOutputRedirectionConfig {
bool apply_volume = false; bool apply_volume = false;
// Any extra delay to apply to the timestamps sent to the redirected output. // Any extra delay to apply to the timestamps sent to the redirected output.
// Note that the dealyed timestamp will be used internally for AV sync. // Note that the delayed timestamp will be used internally for AV sync.
int64_t extra_delay_microseconds = 0; int64_t extra_delay_microseconds = 0;
// Patterns to determine which audio streams should be redirected. If a stream // Patterns to determine which audio streams should be redirected. If a stream
......
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