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;
void AudioOutputRedirector::AddInput(MixerInput* mixer_input) {
if (ApplyToInput(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) {
inputs_.erase(mixer_input);
non_redirected_inputs_.erase(mixer_input);
}
bool AudioOutputRedirector::ApplyToInput(MixerInput* mixer_input) {
......@@ -140,6 +143,33 @@ bool AudioOutputRedirector::ApplyToInput(MixerInput* mixer_input) {
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) {
output_->Start(output_samples_per_second);
}
......@@ -221,8 +251,7 @@ void AudioOutputRedirector::FinishBuffer() {
AudioOutputRedirectorToken* CastMediaShlib::AddAudioOutputRedirection(
const AudioOutputRedirectionConfig& config,
std::unique_ptr<RedirectedAudioOutput> output) {
if (!output || config.num_output_channels <= 0 ||
config.stream_match_patterns.empty()) {
if (!output || config.num_output_channels <= 0) {
return nullptr;
}
......@@ -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 chromecast
......@@ -8,6 +8,7 @@
#include <cstdint>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "base/containers/flat_map.h"
......@@ -60,6 +61,12 @@ class AudioOutputRedirector : public AudioOutputRedirectorToken {
void AddInput(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
// |output_samples_per_second|.
void Start(int output_samples_per_second);
......@@ -88,7 +95,7 @@ class AudioOutputRedirector : public AudioOutputRedirectorToken {
bool ApplyToInput(MixerInput* mixer_input);
const AudioOutputRedirectionConfig config_;
AudioOutputRedirectionConfig config_;
const std::unique_ptr<RedirectedAudioOutput> output_;
int next_num_frames_ = 0;
......@@ -99,6 +106,7 @@ class AudioOutputRedirector : public AudioOutputRedirectorToken {
std::vector<float*> channel_data_;
base::flat_map<MixerInput*, std::unique_ptr<InputImpl>> inputs_;
base::flat_set<MixerInput*> non_redirected_inputs_;
DISALLOW_COPY_AND_ASSIGN(AudioOutputRedirector);
};
......
......@@ -780,6 +780,24 @@ void StreamMixer::RemoveAudioOutputRedirectorOnThread(
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,
SampleFormat format,
int sample_rate,
......
......@@ -9,6 +9,7 @@
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "base/containers/flat_map.h"
......@@ -100,6 +101,10 @@ class StreamMixer {
void AddAudioOutputRedirector(
std::unique_ptr<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|.
void SetVolume(AudioContentType type, float level);
......@@ -194,6 +199,10 @@ class StreamMixer {
void AddAudioOutputRedirectorOnThread(
std::unique_ptr<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,
SampleFormat sample_format,
......
......@@ -385,12 +385,16 @@ class StreamMixerTest : public testing::Test {
}
MockRedirectedAudioOutput* AddOutputRedirector(
const AudioOutputRedirectionConfig& config) {
const AudioOutputRedirectionConfig& config,
AudioOutputRedirector** redirector_ptr = nullptr) {
auto redirected_output =
std::make_unique<MockRedirectedAudioOutput>(kNumChannels);
MockRedirectedAudioOutput* redirected_output_ptr = redirected_output.get();
auto redirector = std::make_unique<AudioOutputRedirector>(
config, std::move(redirected_output));
if (redirector_ptr) {
*redirector_ptr = redirector.get();
}
mixer_->AddAudioOutputRedirector(std::move(redirector));
return redirected_output_ptr;
}
......@@ -1332,6 +1336,64 @@ TEST_F(StreamMixerTest, OutputRedirectionNoMatch) {
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
using StreamMixerDeathTest = StreamMixerTest;
......
......@@ -10,9 +10,11 @@
#include <functional>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "chromecast_export.h"
#include "volume_control.h"
namespace chromecast {
namespace media {
......@@ -176,6 +178,12 @@ class CHROMECAST_EXPORT CastMediaShlib {
// AddAudioOutputRedirection().
static void RemoveAudioOutputRedirection(AudioOutputRedirectorToken* token)
__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
......
......@@ -34,7 +34,7 @@ struct AudioOutputRedirectionConfig {
bool apply_volume = false;
// 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;
// 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