OLD | NEW |
1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chromecast/media/cma/backend/alsa/stream_mixer_alsa.h" | 5 #include "chromecast/media/cma/backend/alsa/stream_mixer_alsa.h" |
6 | 6 |
7 #include <algorithm> | 7 #include <algorithm> |
8 #include <cmath> | 8 #include <cmath> |
9 #include <limits> | 9 #include <limits> |
10 #include <unordered_set> | 10 #include <unordered_set> |
11 #include <utility> | 11 #include <utility> |
12 | 12 |
13 #include "base/bind_helpers.h" | 13 #include "base/bind_helpers.h" |
14 #include "base/command_line.h" | 14 #include "base/command_line.h" |
15 #include "base/lazy_instance.h" | 15 #include "base/lazy_instance.h" |
16 #include "base/memory/ptr_util.h" | 16 #include "base/memory/ptr_util.h" |
17 #include "base/memory/weak_ptr.h" | 17 #include "base/memory/weak_ptr.h" |
18 #include "base/numerics/saturated_arithmetic.h" | 18 #include "base/numerics/saturated_arithmetic.h" |
19 #include "base/single_thread_task_runner.h" | 19 #include "base/single_thread_task_runner.h" |
20 #include "base/strings/string_number_conversions.h" | |
21 #include "base/threading/platform_thread.h" | 20 #include "base/threading/platform_thread.h" |
22 #include "base/threading/thread_task_runner_handle.h" | 21 #include "base/threading/thread_task_runner_handle.h" |
23 #include "chromecast/base/chromecast_switches.h" | 22 #include "chromecast/base/chromecast_switches.h" |
24 #include "chromecast/media/base/audio_device_ids.h" | 23 #include "chromecast/media/base/audio_device_ids.h" |
25 #include "chromecast/media/cma/backend/alsa/alsa_wrapper.h" | 24 #include "chromecast/media/cma/backend/alsa/alsa_wrapper.h" |
26 #include "chromecast/media/cma/backend/alsa/audio_filter_factory.h" | 25 #include "chromecast/media/cma/backend/alsa/audio_filter_factory.h" |
27 #include "chromecast/media/cma/backend/alsa/filter_group.h" | 26 #include "chromecast/media/cma/backend/alsa/filter_group.h" |
28 #include "chromecast/media/cma/backend/alsa/stream_mixer_alsa_input_impl.h" | 27 #include "chromecast/media/cma/backend/alsa/stream_mixer_alsa_input_impl.h" |
29 #include "media/audio/audio_device_description.h" | 28 #include "media/audio/audio_device_description.h" |
30 #include "media/base/audio_bus.h" | 29 #include "media/base/audio_bus.h" |
(...skipping 88 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
119 SND_PCM_FORMAT_S16}; | 118 SND_PCM_FORMAT_S16}; |
120 | 119 |
121 const int64_t kNoTimestamp = std::numeric_limits<int64_t>::min(); | 120 const int64_t kNoTimestamp = std::numeric_limits<int64_t>::min(); |
122 | 121 |
123 int64_t TimespecToMicroseconds(struct timespec time) { | 122 int64_t TimespecToMicroseconds(struct timespec time) { |
124 return static_cast<int64_t>(time.tv_sec) * | 123 return static_cast<int64_t>(time.tv_sec) * |
125 base::Time::kMicrosecondsPerSecond + | 124 base::Time::kMicrosecondsPerSecond + |
126 time.tv_nsec / 1000; | 125 time.tv_nsec / 1000; |
127 } | 126 } |
128 | 127 |
129 bool GetSwitchValueAsInt(const std::string& switch_name, | |
130 int default_value, | |
131 int* value) { | |
132 DCHECK(value); | |
133 *value = default_value; | |
134 if (!base::CommandLine::InitializedForCurrentProcess()) { | |
135 LOG(WARNING) << "No CommandLine for current process."; | |
136 return false; | |
137 } | |
138 const base::CommandLine* command_line = | |
139 base::CommandLine::ForCurrentProcess(); | |
140 if (!command_line->HasSwitch(switch_name)) { | |
141 return false; | |
142 } | |
143 | |
144 int arg_value; | |
145 if (!base::StringToInt(command_line->GetSwitchValueASCII(switch_name), | |
146 &arg_value)) { | |
147 LOG(DFATAL) << "--" << switch_name << " only accepts integers as arguments"; | |
148 return false; | |
149 } | |
150 *value = arg_value; | |
151 return true; | |
152 } | |
153 | |
154 bool GetSwitchValueAsNonNegativeInt(const std::string& switch_name, | |
155 int default_value, | |
156 int* value) { | |
157 DCHECK_GE(default_value, 0) << "--" << switch_name | |
158 << " must have a non-negative default value"; | |
159 DCHECK(value); | |
160 | |
161 if (!GetSwitchValueAsInt(switch_name, default_value, value)) { | |
162 return false; | |
163 } | |
164 | |
165 if (*value < 0) { | |
166 LOG(DFATAL) << "--" << switch_name << " must have a non-negative value"; | |
167 *value = default_value; | |
168 return false; | |
169 } | |
170 return true; | |
171 } | |
172 | |
173 void VectorAccumulate(const int32_t* source, size_t size, int32_t* dest) { | 128 void VectorAccumulate(const int32_t* source, size_t size, int32_t* dest) { |
174 for (size_t i = 0; i < size; ++i) { | 129 for (size_t i = 0; i < size; ++i) { |
175 dest[i] = base::SaturatedAddition(source[i], dest[i]); | 130 dest[i] = base::SaturatedAddition(source[i], dest[i]); |
176 } | 131 } |
177 } | 132 } |
178 | 133 |
179 class StreamMixerAlsaInstance : public StreamMixerAlsa { | 134 class StreamMixerAlsaInstance : public StreamMixerAlsa { |
180 public: | 135 public: |
181 StreamMixerAlsaInstance() {} | 136 StreamMixerAlsaInstance() {} |
182 ~StreamMixerAlsaInstance() override {} | 137 ~StreamMixerAlsaInstance() override {} |
183 | 138 |
184 private: | 139 private: |
185 DISALLOW_COPY_AND_ASSIGN(StreamMixerAlsaInstance); | 140 DISALLOW_COPY_AND_ASSIGN(StreamMixerAlsaInstance); |
186 }; | 141 }; |
187 | 142 |
188 base::LazyInstance<StreamMixerAlsaInstance>::DestructorAtExit g_mixer_instance = | 143 base::LazyInstance<StreamMixerAlsaInstance>::DestructorAtExit g_mixer_instance = |
189 LAZY_INSTANCE_INITIALIZER; | 144 LAZY_INSTANCE_INITIALIZER; |
190 | 145 |
191 } // namespace | 146 } // namespace |
192 | 147 |
| 148 float StreamMixerAlsa::VolumeInfo::GetEffectiveVolume() { |
| 149 return std::min(volume, limit); |
| 150 } |
| 151 |
193 // static | 152 // static |
194 bool StreamMixerAlsa::single_threaded_for_test_ = false; | 153 bool StreamMixerAlsa::single_threaded_for_test_ = false; |
195 | 154 |
196 // static | 155 // static |
197 StreamMixerAlsa* StreamMixerAlsa::Get() { | 156 StreamMixerAlsa* StreamMixerAlsa::Get() { |
198 return g_mixer_instance.Pointer(); | 157 return g_mixer_instance.Pointer(); |
199 } | 158 } |
200 | 159 |
201 // static | 160 // static |
202 void StreamMixerAlsa::MakeSingleThreadedForTest() { | 161 void StreamMixerAlsa::MakeSingleThreadedForTest() { |
203 single_threaded_for_test_ = true; | 162 single_threaded_for_test_ = true; |
204 StreamMixerAlsa::Get()->ResetTaskRunnerForTest(); | 163 StreamMixerAlsa::Get()->ResetTaskRunnerForTest(); |
205 } | 164 } |
206 | 165 |
207 StreamMixerAlsa::StreamMixerAlsa() | 166 StreamMixerAlsa::StreamMixerAlsa() |
208 : mixer_thread_(new base::Thread("ALSA CMA mixer thread")), | 167 : mixer_thread_(new base::Thread("ALSA CMA mixer thread")), |
209 mixer_task_runner_(nullptr), | 168 mixer_task_runner_(nullptr), |
210 requested_output_samples_per_second_(kInvalidSampleRate), | 169 requested_output_samples_per_second_(kInvalidSampleRate), |
211 output_samples_per_second_(kInvalidSampleRate), | 170 output_samples_per_second_(kInvalidSampleRate), |
212 pcm_(nullptr), | 171 pcm_(nullptr), |
213 pcm_hw_params_(nullptr), | 172 pcm_hw_params_(nullptr), |
214 pcm_status_(nullptr), | 173 pcm_status_(nullptr), |
215 pcm_format_(SND_PCM_FORMAT_UNKNOWN), | 174 pcm_format_(SND_PCM_FORMAT_UNKNOWN), |
216 alsa_buffer_size_(0), | 175 alsa_buffer_size_(0), |
217 alsa_period_explicitly_set(false), | |
218 alsa_period_size_(0), | 176 alsa_period_size_(0), |
219 alsa_start_threshold_(0), | 177 alsa_start_threshold_(0), |
220 alsa_avail_min_(0), | 178 alsa_avail_min_(0), |
221 state_(kStateUninitialized), | 179 state_(kStateUninitialized), |
222 retry_write_frames_timer_(new base::Timer(false, false)), | 180 retry_write_frames_timer_(new base::Timer(false, false)), |
223 check_close_timeout_(kDefaultCheckCloseTimeoutMs), | 181 check_close_timeout_(kDefaultCheckCloseTimeoutMs), |
224 check_close_timer_(new base::Timer(false, false)) { | 182 check_close_timer_(new base::Timer(false, false)) { |
225 if (single_threaded_for_test_) { | 183 if (single_threaded_for_test_) { |
226 mixer_task_runner_ = base::ThreadTaskRunnerHandle::Get(); | 184 mixer_task_runner_ = base::ThreadTaskRunnerHandle::Get(); |
227 } else { | 185 } else { |
228 base::Thread::Options options; | 186 base::Thread::Options options; |
229 options.priority = base::ThreadPriority::REALTIME_AUDIO; | 187 options.priority = base::ThreadPriority::REALTIME_AUDIO; |
230 mixer_thread_->StartWithOptions(options); | 188 mixer_thread_->StartWithOptions(options); |
231 mixer_task_runner_ = mixer_thread_->task_runner(); | 189 mixer_task_runner_ = mixer_thread_->task_runner(); |
232 } | 190 } |
233 | 191 |
234 alsa_device_name_ = kOutputDeviceDefaultName; | 192 alsa_device_name_ = kOutputDeviceDefaultName; |
235 if (base::CommandLine::InitializedForCurrentProcess() && | 193 if (base::CommandLine::InitializedForCurrentProcess() && |
236 base::CommandLine::ForCurrentProcess()->HasSwitch( | 194 base::CommandLine::ForCurrentProcess()->HasSwitch( |
237 switches::kAlsaOutputDevice)) { | 195 switches::kAlsaOutputDevice)) { |
238 alsa_device_name_ = | 196 alsa_device_name_ = |
239 base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII( | 197 base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII( |
240 switches::kAlsaOutputDevice); | 198 switches::kAlsaOutputDevice); |
241 } | 199 } |
242 | 200 |
243 int fixed_samples_per_second; | 201 int fixed_samples_per_second = GetSwitchValueNonNegativeInt( |
244 GetSwitchValueAsNonNegativeInt(switches::kAlsaFixedOutputSampleRate, | 202 switches::kAlsaFixedOutputSampleRate, kInvalidSampleRate); |
245 kInvalidSampleRate, &fixed_samples_per_second); | |
246 if (fixed_samples_per_second != kInvalidSampleRate) { | 203 if (fixed_samples_per_second != kInvalidSampleRate) { |
247 LOG(INFO) << "Setting fixed sample rate to " << fixed_samples_per_second; | 204 LOG(INFO) << "Setting fixed sample rate to " << fixed_samples_per_second; |
248 } | 205 } |
249 | 206 |
250 fixed_output_samples_per_second_ = fixed_samples_per_second; | 207 fixed_output_samples_per_second_ = fixed_samples_per_second; |
251 | 208 |
252 low_sample_rate_cutoff_ = | 209 low_sample_rate_cutoff_ = |
253 chromecast::GetSwitchValueBoolean(switches::kAlsaEnableUpsampling, false) | 210 chromecast::GetSwitchValueBoolean(switches::kAlsaEnableUpsampling, false) |
254 ? kLowSampleRateCutoff | 211 ? kLowSampleRateCutoff |
255 : 0; | 212 : 0; |
256 | 213 |
257 // Create filter groups. | 214 // Create filter groups. |
258 // TODO(bshaya): Switch to filter groups based on AudioContentType. | 215 // TODO(bshaya): Switch to filter groups based on AudioContentType. |
259 filter_groups_.push_back(base::MakeUnique<FilterGroup>( | 216 filter_groups_.push_back(base::MakeUnique<FilterGroup>( |
260 std::unordered_set<std::string>( | 217 std::unordered_set<std::string>( |
261 {::media::AudioDeviceDescription::kCommunicationsDeviceId}), | 218 {::media::AudioDeviceDescription::kCommunicationsDeviceId}), |
262 AudioFilterFactory::COMMUNICATION_AUDIO_FILTER)); | 219 AudioFilterFactory::COMMUNICATION_AUDIO_FILTER, |
| 220 AudioContentType::kMedia)); |
263 filter_groups_.push_back(base::MakeUnique<FilterGroup>( | 221 filter_groups_.push_back(base::MakeUnique<FilterGroup>( |
264 std::unordered_set<std::string>({kAlarmAudioDeviceId}), | 222 std::unordered_set<std::string>({kAlarmAudioDeviceId}), |
265 AudioFilterFactory::ALARM_AUDIO_FILTER)); | 223 AudioFilterFactory::ALARM_AUDIO_FILTER, AudioContentType::kAlarm)); |
266 filter_groups_.push_back(base::MakeUnique<FilterGroup>( | 224 filter_groups_.push_back(base::MakeUnique<FilterGroup>( |
267 std::unordered_set<std::string>({kTtsAudioDeviceId}), | 225 std::unordered_set<std::string>({kTtsAudioDeviceId}), |
268 AudioFilterFactory::TTS_AUDIO_FILTER)); | 226 AudioFilterFactory::TTS_AUDIO_FILTER, AudioContentType::kCommunication)); |
269 filter_groups_.push_back(base::MakeUnique<FilterGroup>( | 227 filter_groups_.push_back(base::MakeUnique<FilterGroup>( |
270 std::unordered_set<std::string>( | 228 std::unordered_set<std::string>( |
271 {::media::AudioDeviceDescription::kDefaultDeviceId, | 229 {::media::AudioDeviceDescription::kDefaultDeviceId, |
272 kLocalAudioDeviceId, ""}), | 230 kLocalAudioDeviceId, ""}), |
273 AudioFilterFactory::MEDIA_AUDIO_FILTER)); | 231 AudioFilterFactory::MEDIA_AUDIO_FILTER, AudioContentType::kMedia)); |
274 | 232 |
275 DefineAlsaParameters(); | 233 DefineAlsaParameters(); |
276 } | 234 } |
277 | 235 |
278 void StreamMixerAlsa::ResetTaskRunnerForTest() { | 236 void StreamMixerAlsa::ResetTaskRunnerForTest() { |
279 mixer_task_runner_ = base::ThreadTaskRunnerHandle::Get(); | 237 mixer_task_runner_ = base::ThreadTaskRunnerHandle::Get(); |
280 } | 238 } |
281 | 239 |
282 void StreamMixerAlsa::DefineAlsaParameters() { | 240 void StreamMixerAlsa::DefineAlsaParameters() { |
283 // Get the ALSA output configuration from the command line. | 241 // Get the ALSA output configuration from the command line. |
284 int buffer_size; | 242 alsa_buffer_size_ = GetSwitchValueNonNegativeInt( |
285 GetSwitchValueAsNonNegativeInt(switches::kAlsaOutputBufferSize, | 243 switches::kAlsaOutputBufferSize, kDefaultOutputBufferSizeFrames); |
286 kDefaultOutputBufferSizeFrames, &buffer_size); | |
287 alsa_buffer_size_ = buffer_size; | |
288 | 244 |
289 int period_size; | 245 alsa_period_size_ = GetSwitchValueNonNegativeInt( |
290 if (GetSwitchValueAsNonNegativeInt(switches::kAlsaOutputPeriodSize, | 246 switches::kAlsaOutputPeriodSize, alsa_buffer_size_ / 16); |
291 alsa_buffer_size_ / 16, &period_size)) { | 247 if (alsa_period_size_ >= alsa_buffer_size_) { |
292 if (period_size >= buffer_size) { | 248 LOG(DFATAL) << "ALSA period size must be smaller than the buffer size"; |
293 LOG(DFATAL) << "ALSA period size must be smaller than the buffer size"; | 249 alsa_period_size_ = alsa_buffer_size_ / 2; |
294 period_size = buffer_size / 2; | |
295 } else { | |
296 alsa_period_explicitly_set = true; | |
297 } | |
298 } | 250 } |
299 alsa_period_size_ = period_size; | |
300 | 251 |
301 int start_threshold; | 252 alsa_start_threshold_ = GetSwitchValueNonNegativeInt( |
302 GetSwitchValueAsNonNegativeInt(switches::kAlsaOutputStartThreshold, | 253 switches::kAlsaOutputStartThreshold, |
303 (buffer_size / period_size) * period_size, | 254 (alsa_buffer_size_ / alsa_period_size_) * alsa_period_size_); |
304 &start_threshold); | 255 if (alsa_start_threshold_ > alsa_buffer_size_) { |
305 if (start_threshold > buffer_size) { | |
306 LOG(DFATAL) << "ALSA start threshold must be no larger than " | 256 LOG(DFATAL) << "ALSA start threshold must be no larger than " |
307 << "the buffer size"; | 257 << "the buffer size"; |
308 start_threshold = (buffer_size / period_size) * period_size; | 258 alsa_start_threshold_ = |
| 259 (alsa_buffer_size_ / alsa_period_size_) * alsa_period_size_; |
309 } | 260 } |
310 alsa_start_threshold_ = start_threshold; | |
311 | 261 |
312 // By default, allow the transfer when at least period_size samples can be | 262 // By default, allow the transfer when at least period_size samples can be |
313 // processed. | 263 // processed. |
314 int avail_min; | 264 alsa_avail_min_ = GetSwitchValueNonNegativeInt(switches::kAlsaOutputAvailMin, |
315 GetSwitchValueAsNonNegativeInt(switches::kAlsaOutputAvailMin, period_size, | 265 alsa_period_size_); |
316 &avail_min); | 266 if (alsa_avail_min_ > alsa_buffer_size_) { |
317 if (avail_min > buffer_size) { | |
318 LOG(DFATAL) << "ALSA avail min must be no larger than the buffer size"; | 267 LOG(DFATAL) << "ALSA avail min must be no larger than the buffer size"; |
319 avail_min = alsa_period_size_; | 268 alsa_avail_min_ = alsa_period_size_; |
320 } | 269 } |
321 alsa_avail_min_ = avail_min; | |
322 | 270 |
323 // --accept-resource-provider should imply a check close timeout of 0. | 271 // --accept-resource-provider should imply a check close timeout of 0. |
324 int default_close_timeout = chromecast::GetSwitchValueBoolean( | 272 int default_close_timeout = chromecast::GetSwitchValueBoolean( |
325 switches::kAcceptResourceProvider, false) | 273 switches::kAcceptResourceProvider, false) |
326 ? 0 | 274 ? 0 |
327 : kDefaultCheckCloseTimeoutMs; | 275 : kDefaultCheckCloseTimeoutMs; |
328 GetSwitchValueAsInt(switches::kAlsaCheckCloseTimeout, default_close_timeout, | 276 check_close_timeout_ = GetSwitchValueInt(switches::kAlsaCheckCloseTimeout, |
329 &check_close_timeout_); | 277 default_close_timeout); |
330 } | 278 } |
331 | 279 |
332 unsigned int StreamMixerAlsa::DetermineOutputRate(unsigned int requested_rate) { | 280 unsigned int StreamMixerAlsa::DetermineOutputRate(unsigned int requested_rate) { |
333 if (fixed_output_samples_per_second_ != kInvalidSampleRate) { | 281 if (fixed_output_samples_per_second_ != kInvalidSampleRate) { |
334 LOG(INFO) << "Requested output rate is " << requested_rate; | 282 LOG(INFO) << "Requested output rate is " << requested_rate; |
335 LOG(INFO) << "Cannot change rate since it is fixed to " | 283 LOG(INFO) << "Cannot change rate since it is fixed to " |
336 << fixed_output_samples_per_second_; | 284 << fixed_output_samples_per_second_; |
337 return fixed_output_samples_per_second_; | 285 return fixed_output_samples_per_second_; |
338 } | 286 } |
339 | 287 |
(...skipping 95 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
435 snd_pcm_uframes_t requested_buffer_size = alsa_buffer_size_; | 383 snd_pcm_uframes_t requested_buffer_size = alsa_buffer_size_; |
436 RETURN_ERROR_CODE(PcmHwParamsSetBufferSizeNear, pcm_, pcm_hw_params_, | 384 RETURN_ERROR_CODE(PcmHwParamsSetBufferSizeNear, pcm_, pcm_hw_params_, |
437 &alsa_buffer_size_); | 385 &alsa_buffer_size_); |
438 if (requested_buffer_size != alsa_buffer_size_) { | 386 if (requested_buffer_size != alsa_buffer_size_) { |
439 LOG(WARNING) << "Requested buffer size (" << requested_buffer_size | 387 LOG(WARNING) << "Requested buffer size (" << requested_buffer_size |
440 << " frames) does not match the actual buffer size (" | 388 << " frames) does not match the actual buffer size (" |
441 << alsa_buffer_size_ | 389 << alsa_buffer_size_ |
442 << " frames). This may lead to an increase in " | 390 << " frames). This may lead to an increase in " |
443 "either audio latency or audio underruns."; | 391 "either audio latency or audio underruns."; |
444 | 392 |
445 // Always try to use the value for period_size that was passed in on the | 393 if (alsa_period_size_ >= alsa_buffer_size_) { |
446 // command line, if any. | |
447 if (!alsa_period_explicitly_set) { | |
448 alsa_period_size_ = alsa_buffer_size_ / 16; | |
449 } else if (alsa_period_size_ >= alsa_buffer_size_) { | |
450 snd_pcm_uframes_t new_period_size = alsa_buffer_size_ / 2; | 394 snd_pcm_uframes_t new_period_size = alsa_buffer_size_ / 2; |
451 LOG(DFATAL) << "Configured period size (" << alsa_period_size_ | 395 LOG(DFATAL) << "Configured period size (" << alsa_period_size_ |
452 << ") is >= actual buffer size (" << alsa_buffer_size_ | 396 << ") is >= actual buffer size (" << alsa_buffer_size_ |
453 << "); reducing to " << new_period_size; | 397 << "); reducing to " << new_period_size; |
454 alsa_period_size_ = new_period_size; | 398 alsa_period_size_ = new_period_size; |
455 } | 399 } |
456 // Scale the start threshold and avail_min based on the new buffer size. | 400 // Scale the start threshold and avail_min based on the new buffer size. |
457 float original_buffer_size = static_cast<float>(requested_buffer_size); | 401 float original_buffer_size = static_cast<float>(requested_buffer_size); |
458 float avail_min_ratio = original_buffer_size / alsa_avail_min_; | 402 float avail_min_ratio = original_buffer_size / alsa_avail_min_; |
459 alsa_avail_min_ = alsa_buffer_size_ / avail_min_ratio; | 403 alsa_avail_min_ = alsa_buffer_size_ / avail_min_ratio; |
(...skipping 193 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
653 DCHECK(input); | 597 DCHECK(input); |
654 | 598 |
655 // If the new input is a primary one, we may need to change the output | 599 // If the new input is a primary one, we may need to change the output |
656 // sample rate to match its input sample rate. | 600 // sample rate to match its input sample rate. |
657 // We only change the output rate if it is not set to a fixed value. | 601 // We only change the output rate if it is not set to a fixed value. |
658 if (input->primary() && | 602 if (input->primary() && |
659 fixed_output_samples_per_second_ == kInvalidSampleRate) { | 603 fixed_output_samples_per_second_ == kInvalidSampleRate) { |
660 CheckChangeOutputRate(input->input_samples_per_second()); | 604 CheckChangeOutputRate(input->input_samples_per_second()); |
661 } | 605 } |
662 | 606 |
| 607 auto type = input->content_type(); |
| 608 if (input->primary()) { |
| 609 input->SetContentTypeVolume(volume_info_[type].GetEffectiveVolume()); |
| 610 } else { |
| 611 input->SetContentTypeVolume(volume_info_[type].volume); |
| 612 } |
| 613 input->SetMuted(volume_info_[type].muted); |
| 614 |
663 check_close_timer_->Stop(); | 615 check_close_timer_->Stop(); |
664 switch (state_) { | 616 switch (state_) { |
665 case kStateUninitialized: | 617 case kStateUninitialized: |
666 requested_output_samples_per_second_ = input->input_samples_per_second(); | 618 requested_output_samples_per_second_ = input->input_samples_per_second(); |
667 Start(); | 619 Start(); |
668 // Fallthrough intended | 620 // Fallthrough intended |
669 case kStateNormalPlayback: { | 621 case kStateNormalPlayback: { |
670 bool found_filter_group = false; | 622 bool found_filter_group = false; |
671 input->Initialize(rendering_delay_); | 623 input->Initialize(rendering_delay_); |
672 for (auto&& filter_group : filter_groups_) { | 624 for (auto&& filter_group : filter_groups_) { |
(...skipping 287 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
960 CastMediaShlib::LoopbackAudioObserver* observer) { | 912 CastMediaShlib::LoopbackAudioObserver* observer) { |
961 RUN_ON_MIXER_THREAD(&StreamMixerAlsa::RemoveLoopbackAudioObserver, observer); | 913 RUN_ON_MIXER_THREAD(&StreamMixerAlsa::RemoveLoopbackAudioObserver, observer); |
962 DCHECK(std::find(loopback_observers_.begin(), loopback_observers_.end(), | 914 DCHECK(std::find(loopback_observers_.begin(), loopback_observers_.end(), |
963 observer) != loopback_observers_.end()); | 915 observer) != loopback_observers_.end()); |
964 loopback_observers_.erase(std::remove(loopback_observers_.begin(), | 916 loopback_observers_.erase(std::remove(loopback_observers_.begin(), |
965 loopback_observers_.end(), observer), | 917 loopback_observers_.end(), observer), |
966 loopback_observers_.end()); | 918 loopback_observers_.end()); |
967 observer->OnRemoved(); | 919 observer->OnRemoved(); |
968 } | 920 } |
969 | 921 |
| 922 void StreamMixerAlsa::SetVolume(AudioContentType type, float level) { |
| 923 RUN_ON_MIXER_THREAD(&StreamMixerAlsa::SetVolume, type, level); |
| 924 volume_info_[type].volume = level; |
| 925 float effective_volume = volume_info_[type].GetEffectiveVolume(); |
| 926 for (auto&& input : inputs_) { |
| 927 if (input->content_type() == type) { |
| 928 if (input->primary()) { |
| 929 input->SetContentTypeVolume(effective_volume); |
| 930 } else { |
| 931 // Volume limits don't apply to effects streams. |
| 932 input->SetContentTypeVolume(level); |
| 933 } |
| 934 } |
| 935 } |
| 936 |
| 937 for (auto&& filter : filter_groups_) { |
| 938 if (filter->content_type() == type) { |
| 939 filter->set_volume(effective_volume); |
| 940 } |
| 941 } |
| 942 } |
| 943 |
| 944 void StreamMixerAlsa::SetMuted(AudioContentType type, bool muted) { |
| 945 RUN_ON_MIXER_THREAD(&StreamMixerAlsa::SetMuted, type, muted); |
| 946 volume_info_[type].muted = muted; |
| 947 for (auto&& input : inputs_) { |
| 948 if (input->content_type() == type) { |
| 949 input->SetMuted(muted); |
| 950 } |
| 951 } |
| 952 } |
| 953 |
| 954 void StreamMixerAlsa::SetOutputLimit(AudioContentType type, float limit) { |
| 955 RUN_ON_MIXER_THREAD(&StreamMixerAlsa::SetOutputLimit, type, limit); |
| 956 LOG(INFO) << "Set volume limit for " << static_cast<int>(type) << " to " |
| 957 << limit; |
| 958 volume_info_[type].limit = limit; |
| 959 float effective_volume = volume_info_[type].GetEffectiveVolume(); |
| 960 for (auto&& input : inputs_) { |
| 961 // Volume limits don't apply to effects streams. |
| 962 if (input->primary() && input->content_type() == type) { |
| 963 input->SetContentTypeVolume(effective_volume); |
| 964 } |
| 965 } |
| 966 |
| 967 for (auto&& filter : filter_groups_) { |
| 968 if (filter->content_type() == type) { |
| 969 filter->set_volume(effective_volume); |
| 970 } |
| 971 } |
| 972 } |
| 973 |
970 } // namespace media | 974 } // namespace media |
971 } // namespace chromecast | 975 } // namespace chromecast |
OLD | NEW |