Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1813)

Unified Diff: chromecast/media/cma/backend/alsa/stream_mixer_alsa.cc

Issue 2701613006: [Chromecast] Process streams with different post-processing. (Closed)
Patch Set: constexpr Created 3 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: chromecast/media/cma/backend/alsa/stream_mixer_alsa.cc
diff --git a/chromecast/media/cma/backend/alsa/stream_mixer_alsa.cc b/chromecast/media/cma/backend/alsa/stream_mixer_alsa.cc
index 463a300aba2498a5aca63e6438cf046cc7c816c3..8e4c352a388b99232615cbd4a24f7e744911944b 100644
--- a/chromecast/media/cma/backend/alsa/stream_mixer_alsa.cc
+++ b/chromecast/media/cma/backend/alsa/stream_mixer_alsa.cc
@@ -7,20 +7,26 @@
#include <algorithm>
#include <cmath>
#include <limits>
+#include <unordered_set>
#include <utility>
#include "base/bind_helpers.h"
#include "base/command_line.h"
#include "base/lazy_instance.h"
+#include "base/memory/ptr_util.h"
#include "base/memory/weak_ptr.h"
+#include "base/numerics/saturated_arithmetic.h"
#include "base/single_thread_task_runner.h"
#include "base/strings/string_number_conversions.h"
#include "base/threading/platform_thread.h"
#include "base/threading/thread_task_runner_handle.h"
#include "chromecast/base/chromecast_switches.h"
+#include "chromecast/media/base/audio_device_ids.h"
#include "chromecast/media/cma/backend/alsa/alsa_wrapper.h"
#include "chromecast/media/cma/backend/alsa/audio_filter_factory.h"
+#include "chromecast/media/cma/backend/alsa/filter_group.h"
#include "chromecast/media/cma/backend/alsa/stream_mixer_alsa_input_impl.h"
+#include "media/audio/audio_device_description.h"
#include "media/base/audio_bus.h"
#include "media/base/media_switches.h"
@@ -112,9 +118,6 @@ static int* kAlsaDirDontCare = nullptr;
const snd_pcm_format_t kPreferredSampleFormats[] = {SND_PCM_FORMAT_S32,
SND_PCM_FORMAT_S16};
-// How many seconds of silence should be passed to the filters to flush them.
-const float kSilenceSecondsToFilter = 1.0f;
-
const int64_t kNoTimestamp = std::numeric_limits<int64_t>::min();
int64_t TimespecToMicroseconds(struct timespec time) {
@@ -167,6 +170,12 @@ bool GetSwitchValueAsNonNegativeInt(const std::string& switch_name,
return true;
}
+void VectorAccumulate(const int32_t* source, size_t size, int32_t* dest) {
+ for (size_t i = 0; i < size; ++i) {
+ dest[i] = base::SaturatedAddition(source[i], dest[i]);
+ }
+}
+
class StreamMixerAlsaInstance : public StreamMixerAlsa {
public:
StreamMixerAlsaInstance() {}
@@ -245,11 +254,22 @@ StreamMixerAlsa::StreamMixerAlsa()
? kLowSampleRateCutoff
: 0;
- // Create filters
- pre_loopback_filter_ = AudioFilterFactory::MakeAudioFilter(
- AudioFilterFactory::PRE_LOOPBACK_FILTER);
- post_loopback_filter_ = AudioFilterFactory::MakeAudioFilter(
- AudioFilterFactory::POST_LOOPBACK_FILTER);
+ // Create filter groups.
+ filter_groups_.push_back(base::MakeUnique<FilterGroup>(
+ std::unordered_set<std::string>(
+ {::media::AudioDeviceDescription::kCommunicationsDeviceId}),
+ AudioFilterFactory::COMMUNICATION_AUDIO_FILTER));
+ filter_groups_.push_back(base::MakeUnique<FilterGroup>(
+ std::unordered_set<std::string>({kAlarmAudioDeviceId}),
+ AudioFilterFactory::ALARM_AUDIO_FILTER));
+ filter_groups_.push_back(base::MakeUnique<FilterGroup>(
+ std::unordered_set<std::string>({kTtsAudioDeviceId}),
+ AudioFilterFactory::TTS_AUDIO_FILTER));
+ filter_groups_.push_back(base::MakeUnique<FilterGroup>(
+ std::unordered_set<std::string>(
+ {::media::AudioDeviceDescription::kDefaultDeviceId,
+ kEarconAudioDeviceId}),
+ AudioFilterFactory::MEDIA_AUDIO_FILTER));
DefineAlsaParameters();
}
@@ -529,14 +549,9 @@ void StreamMixerAlsa::Start() {
}
// Initialize filters
- if (pre_loopback_filter_) {
- pre_loopback_filter_->SetSampleRateAndFormat(
- output_samples_per_second_, ::media::SampleFormat::kSampleFormatS32);
- }
-
- if (post_loopback_filter_) {
- post_loopback_filter_->SetSampleRateAndFormat(
- output_samples_per_second_, ::media::SampleFormat::kSampleFormatS32);
+ for (auto&& filter_group : filter_groups_) {
+ filter_group->Initialize(output_samples_per_second_,
+ ::media::SampleFormat::kSampleFormatS32);
}
RETURN_REPORT_ERROR(PcmPrepare, pcm_);
@@ -635,6 +650,9 @@ void StreamMixerAlsa::AddInput(std::unique_ptr<InputQueue> input) {
}
DCHECK(input);
+
+ LOG(INFO) << __FUNCTION__;
kmackay 2017/02/24 18:03:55 Necessary logging?
bshaya 2017/02/27 17:00:15 Done.
+
// If the new input is a primary one, we may need to change the output
// sample rate to match its input sample rate.
// We only change the output rate if it is not set to a fixed value.
@@ -644,17 +662,31 @@ void StreamMixerAlsa::AddInput(std::unique_ptr<InputQueue> input) {
}
check_close_timer_->Stop();
- if (state_ == kStateUninitialized) {
- requested_output_samples_per_second_ = input->input_samples_per_second();
- Start();
- input->Initialize(rendering_delay_);
- inputs_.push_back(std::move(input));
- } else if (state_ == kStateNormalPlayback) {
- input->Initialize(rendering_delay_);
- inputs_.push_back(std::move(input));
- } else {
- input->SignalError(StreamMixerAlsaInput::MixerError::kInternalError);
- ignored_inputs_.push_back(std::move(input));
+ bool found_filter_group = false;
kmackay 2017/02/24 18:03:55 Move down into the case statement? (you'll need br
bshaya 2017/02/27 17:00:15 Done.
+ switch (state_) {
+ case kStateUninitialized:
+ requested_output_samples_per_second_ = input->input_samples_per_second();
+ Start();
+ // Fallthrough intended
+ case kStateNormalPlayback:
+ input->Initialize(rendering_delay_);
+ for (auto&& filter_group : filter_groups_) {
+ if (filter_group->CanProcessInput(input.get())) {
+ found_filter_group = true;
+ input->set_filter_group(filter_group.get());
+ break;
+ }
+ }
+ DCHECK(found_filter_group) << "Could not find a filter group for "
+ << input->device_id();
+ inputs_.push_back(std::move(input));
+ break;
+ case kStateError:
+ input->SignalError(StreamMixerAlsaInput::MixerError::kInternalError);
+ ignored_inputs_.push_back(std::move(input));
+ break;
+ default:
+ NOTREACHED();
}
}
@@ -762,6 +794,8 @@ void StreamMixerAlsa::WriteFrames() {
bool StreamMixerAlsa::TryWriteFrames() {
DCHECK(mixer_task_runner_->BelongsToCurrentThread());
+ DCHECK_GE(filter_groups_.size(), 1u);
+
if (state_ != kStateNormalPlayback) {
return false;
}
@@ -769,12 +803,14 @@ bool StreamMixerAlsa::TryWriteFrames() {
const int min_frames_in_buffer =
output_samples_per_second_ * kMinBufferedDataMs / 1000;
int chunk_size = output_samples_per_second_ * kMaxWriteSizeMs / 1000;
- std::vector<InputQueue*> active_inputs;
+ bool is_silence = true;
for (auto&& input : inputs_) {
int read_size = input->MaxReadSize();
if (read_size > 0) {
- active_inputs.push_back(input.get());
+ DCHECK(input->filter_group());
+ input->filter_group()->AddActiveInput(input.get());
chunk_size = std::min(chunk_size, read_size);
+ is_silence = false;
} else if (input->primary()) {
if (alsa_->PcmStatus(pcm_, pcm_status_) != 0) {
LOG(ERROR) << "Failed to get status";
@@ -801,57 +837,52 @@ bool StreamMixerAlsa::TryWriteFrames() {
}
}
- if (active_inputs.empty()) {
- // No inputs have any data to provide. Fill with silence to avoid underrun.
+ if (is_silence) {
+ // No inputs have any data to provide. Push silence to prevent underrun.
chunk_size = kPreventUnderrunChunkSize;
- if (!mixed_ || mixed_->frames() < chunk_size) {
- mixed_ = ::media::AudioBus::Create(kNumOutputChannels, chunk_size);
- }
-
- mixed_->Zero();
- WriteMixedPcm(*mixed_, chunk_size, true /* is_silence */);
- return true;
- }
-
- // If |mixed_| has not been allocated, or it is too small, allocate a buffer.
- if (!mixed_ || mixed_->frames() < chunk_size) {
- mixed_ = ::media::AudioBus::Create(kNumOutputChannels, chunk_size);
}
- // If |temp_| has not been allocated, or is too small, allocate a buffer.
- if (!temp_ || temp_->frames() < chunk_size) {
- temp_ = ::media::AudioBus::Create(kNumOutputChannels, chunk_size);
+ // Mix and filter each group.
+ std::vector<uint8_t>* interleaved = nullptr;
+ for (auto&& filter_group : filter_groups_) {
+ if (filter_group->MixAndFilter(chunk_size)) {
+ if (!interleaved) {
+ interleaved = filter_group->GetInterleaved();
+ } else {
+ DCHECK_EQ(4, BytesPerOutputFormatSample());
+ VectorAccumulate(
+ reinterpret_cast<int32_t*>(filter_group->GetInterleaved()->data()),
+ chunk_size * kNumOutputChannels,
+ reinterpret_cast<int32_t*>(interleaved->data()));
+ }
+ }
}
- mixed_->ZeroFramesPartial(0, chunk_size);
-
- // Loop through active inputs, polling them for data, and mixing them.
- for (InputQueue* input : active_inputs) {
- input->GetResampledData(temp_.get(), chunk_size);
- for (int c = 0; c < kNumOutputChannels; ++c) {
- input->VolumeScaleAccumulate(c, temp_->channel(c), chunk_size,
- mixed_->channel(c));
- }
+ if (!interleaved) {
+ // No group has any data, write empty buffer.
+ filter_groups_[0]->ClearInterleaved(chunk_size);
+ interleaved = filter_groups_[0]->GetInterleaved();
}
- WriteMixedPcm(*mixed_, chunk_size, false /* is_silence */);
+ WriteMixedPcm(interleaved, chunk_size);
return true;
}
+size_t StreamMixerAlsa::InterleavedSize(int frames) {
+ return BytesPerOutputFormatSample() *
+ static_cast<size_t>(frames * kNumOutputChannels);
+}
+
ssize_t StreamMixerAlsa::BytesPerOutputFormatSample() {
return alsa_->PcmFormatSize(pcm_format_, 1);
}
-void StreamMixerAlsa::WriteMixedPcm(const ::media::AudioBus& mixed,
- int frames, bool is_silence) {
+void StreamMixerAlsa::WriteMixedPcm(std::vector<uint8_t>* interleaved,
+ int frames) {
DCHECK(mixer_task_runner_->BelongsToCurrentThread());
CHECK_PCM_INITIALIZED();
-
- size_t interleaved_size = static_cast<size_t>(frames * kNumOutputChannels) *
- BytesPerOutputFormatSample();
- if (interleaved_.size() < interleaved_size) {
- interleaved_.resize(interleaved_size);
- }
+ DCHECK(interleaved);
+ DCHECK_GE(interleaved->size(), InterleavedSize(frames));
int64_t expected_playback_time;
if (rendering_delay_.timestamp_microseconds == kNoTimestamp) {
@@ -861,37 +892,10 @@ void StreamMixerAlsa::WriteMixedPcm(const ::media::AudioBus& mixed,
rendering_delay_.delay_microseconds;
}
- mixed.ToInterleaved(frames, BytesPerOutputFormatSample(),
- interleaved_.data());
-
- // Ensure that, on onset of silence, at least |kSilenceSecondsToFilter|
- // second of audio get pushed through the filters to clear any memory.
- bool filter_frames = true;
- if (is_silence) {
- int silence_frames_to_filter =
- output_samples_per_second_ * kSilenceSecondsToFilter;
- if (silence_frames_filtered_ < silence_frames_to_filter) {
- silence_frames_filtered_ += frames;
- } else {
- filter_frames = false;
- }
- } else {
- silence_frames_filtered_ = 0;
- }
-
- // Filter, send to observers, and post filter
- if (pre_loopback_filter_ && filter_frames) {
- pre_loopback_filter_->ProcessInterleaved(interleaved_.data(), frames);
- }
-
for (CastMediaShlib::LoopbackAudioObserver* observer : loopback_observers_) {
observer->OnLoopbackAudio(expected_playback_time, kSampleFormatS32,
output_samples_per_second_, kNumOutputChannels,
- interleaved_.data(), interleaved_size);
- }
-
- if (post_loopback_filter_ && filter_frames) {
- post_loopback_filter_->ProcessInterleaved(interleaved_.data(), frames);
+ interleaved->data(), InterleavedSize(frames));
}
// If the PCM has been drained it will be in SND_PCM_STATE_SETUP and need
@@ -901,7 +905,7 @@ void StreamMixerAlsa::WriteMixedPcm(const ::media::AudioBus& mixed,
}
int frames_left = frames;
- uint8_t* data = &interleaved_[0];
+ uint8_t* data = interleaved->data();
while (frames_left) {
int frames_or_error;
while ((frames_or_error = alsa_->PcmWritei(pcm_, data, frames_left)) < 0) {

Powered by Google App Engine
This is Rietveld 408576698