Chromium Code Reviews| 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 {} |
| (...skipping 24 matching lines...) Expand all Loading... | |
| 207 StreamMixerAlsa::StreamMixerAlsa() | 162 StreamMixerAlsa::StreamMixerAlsa() |
| 208 : mixer_thread_(new base::Thread("ALSA CMA mixer thread")), | 163 : mixer_thread_(new base::Thread("ALSA CMA mixer thread")), |
| 209 mixer_task_runner_(nullptr), | 164 mixer_task_runner_(nullptr), |
| 210 requested_output_samples_per_second_(kInvalidSampleRate), | 165 requested_output_samples_per_second_(kInvalidSampleRate), |
| 211 output_samples_per_second_(kInvalidSampleRate), | 166 output_samples_per_second_(kInvalidSampleRate), |
| 212 pcm_(nullptr), | 167 pcm_(nullptr), |
| 213 pcm_hw_params_(nullptr), | 168 pcm_hw_params_(nullptr), |
| 214 pcm_status_(nullptr), | 169 pcm_status_(nullptr), |
| 215 pcm_format_(SND_PCM_FORMAT_UNKNOWN), | 170 pcm_format_(SND_PCM_FORMAT_UNKNOWN), |
| 216 alsa_buffer_size_(0), | 171 alsa_buffer_size_(0), |
| 217 alsa_period_explicitly_set(false), | |
| 218 alsa_period_size_(0), | 172 alsa_period_size_(0), |
| 219 alsa_start_threshold_(0), | 173 alsa_start_threshold_(0), |
| 220 alsa_avail_min_(0), | 174 alsa_avail_min_(0), |
| 221 state_(kStateUninitialized), | 175 state_(kStateUninitialized), |
| 222 retry_write_frames_timer_(new base::Timer(false, false)), | 176 retry_write_frames_timer_(new base::Timer(false, false)), |
| 223 check_close_timeout_(kDefaultCheckCloseTimeoutMs), | 177 check_close_timeout_(kDefaultCheckCloseTimeoutMs), |
| 224 check_close_timer_(new base::Timer(false, false)) { | 178 check_close_timer_(new base::Timer(false, false)) { |
| 179 for (auto type : {AudioContentType::kMedia, AudioContentType::kAlarm, | |
|
slan
2017/03/13 22:09:51
Perhaps we should use a static assert in here to m
kmackay
2017/03/14 00:20:59
Done.
| |
| 180 AudioContentType::kCommunication}) { | |
| 181 volume_[type] = 1.0f; | |
| 182 volume_limit_[type] = 1.0f; | |
| 183 muted_[type] = false; | |
| 184 } | |
| 185 | |
| 225 if (single_threaded_for_test_) { | 186 if (single_threaded_for_test_) { |
| 226 mixer_task_runner_ = base::ThreadTaskRunnerHandle::Get(); | 187 mixer_task_runner_ = base::ThreadTaskRunnerHandle::Get(); |
| 227 } else { | 188 } else { |
| 228 base::Thread::Options options; | 189 base::Thread::Options options; |
| 229 options.priority = base::ThreadPriority::REALTIME_AUDIO; | 190 options.priority = base::ThreadPriority::REALTIME_AUDIO; |
| 230 mixer_thread_->StartWithOptions(options); | 191 mixer_thread_->StartWithOptions(options); |
| 231 mixer_task_runner_ = mixer_thread_->task_runner(); | 192 mixer_task_runner_ = mixer_thread_->task_runner(); |
| 232 } | 193 } |
| 233 | 194 |
| 234 alsa_device_name_ = kOutputDeviceDefaultName; | 195 alsa_device_name_ = kOutputDeviceDefaultName; |
| 235 if (base::CommandLine::InitializedForCurrentProcess() && | 196 if (base::CommandLine::InitializedForCurrentProcess() && |
| 236 base::CommandLine::ForCurrentProcess()->HasSwitch( | 197 base::CommandLine::ForCurrentProcess()->HasSwitch( |
| 237 switches::kAlsaOutputDevice)) { | 198 switches::kAlsaOutputDevice)) { |
| 238 alsa_device_name_ = | 199 alsa_device_name_ = |
| 239 base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII( | 200 base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII( |
| 240 switches::kAlsaOutputDevice); | 201 switches::kAlsaOutputDevice); |
| 241 } | 202 } |
| 242 | 203 |
| 243 int fixed_samples_per_second; | 204 int fixed_samples_per_second = GetSwitchValueNonNegativeInt( |
| 244 GetSwitchValueAsNonNegativeInt(switches::kAlsaFixedOutputSampleRate, | 205 switches::kAlsaFixedOutputSampleRate, kInvalidSampleRate); |
| 245 kInvalidSampleRate, &fixed_samples_per_second); | |
| 246 if (fixed_samples_per_second != kInvalidSampleRate) { | 206 if (fixed_samples_per_second != kInvalidSampleRate) { |
| 247 LOG(INFO) << "Setting fixed sample rate to " << fixed_samples_per_second; | 207 LOG(INFO) << "Setting fixed sample rate to " << fixed_samples_per_second; |
| 248 } | 208 } |
| 249 | 209 |
| 250 fixed_output_samples_per_second_ = fixed_samples_per_second; | 210 fixed_output_samples_per_second_ = fixed_samples_per_second; |
| 251 | 211 |
| 252 low_sample_rate_cutoff_ = | 212 low_sample_rate_cutoff_ = |
| 253 chromecast::GetSwitchValueBoolean(switches::kAlsaEnableUpsampling, false) | 213 chromecast::GetSwitchValueBoolean(switches::kAlsaEnableUpsampling, false) |
| 254 ? kLowSampleRateCutoff | 214 ? kLowSampleRateCutoff |
| 255 : 0; | 215 : 0; |
| 256 | 216 |
| 257 // Create filter groups. | 217 // Create filter groups. |
| 258 // TODO(bshaya): Switch to filter groups based on AudioContentType. | 218 // TODO(bshaya): Switch to filter groups based on AudioContentType. |
| 259 filter_groups_.push_back(base::MakeUnique<FilterGroup>( | 219 filter_groups_.push_back(base::MakeUnique<FilterGroup>( |
| 260 std::unordered_set<std::string>( | 220 std::unordered_set<std::string>( |
| 261 {::media::AudioDeviceDescription::kCommunicationsDeviceId}), | 221 {::media::AudioDeviceDescription::kCommunicationsDeviceId}), |
| 262 AudioFilterFactory::COMMUNICATION_AUDIO_FILTER)); | 222 AudioFilterFactory::COMMUNICATION_AUDIO_FILTER, |
| 223 AudioContentType::kMedia)); | |
| 263 filter_groups_.push_back(base::MakeUnique<FilterGroup>( | 224 filter_groups_.push_back(base::MakeUnique<FilterGroup>( |
| 264 std::unordered_set<std::string>({kAlarmAudioDeviceId}), | 225 std::unordered_set<std::string>({kAlarmAudioDeviceId}), |
| 265 AudioFilterFactory::ALARM_AUDIO_FILTER)); | 226 AudioFilterFactory::ALARM_AUDIO_FILTER, AudioContentType::kAlarm)); |
| 266 filter_groups_.push_back(base::MakeUnique<FilterGroup>( | 227 filter_groups_.push_back(base::MakeUnique<FilterGroup>( |
| 267 std::unordered_set<std::string>({kTtsAudioDeviceId}), | 228 std::unordered_set<std::string>({kTtsAudioDeviceId}), |
| 268 AudioFilterFactory::TTS_AUDIO_FILTER)); | 229 AudioFilterFactory::TTS_AUDIO_FILTER, AudioContentType::kCommunication)); |
| 269 filter_groups_.push_back(base::MakeUnique<FilterGroup>( | 230 filter_groups_.push_back(base::MakeUnique<FilterGroup>( |
| 270 std::unordered_set<std::string>( | 231 std::unordered_set<std::string>( |
| 271 {::media::AudioDeviceDescription::kDefaultDeviceId, | 232 {::media::AudioDeviceDescription::kDefaultDeviceId, |
| 272 kLocalAudioDeviceId, ""}), | 233 kLocalAudioDeviceId, ""}), |
| 273 AudioFilterFactory::MEDIA_AUDIO_FILTER)); | 234 AudioFilterFactory::MEDIA_AUDIO_FILTER, AudioContentType::kMedia)); |
| 274 | 235 |
| 275 DefineAlsaParameters(); | 236 DefineAlsaParameters(); |
| 276 } | 237 } |
| 277 | 238 |
| 278 void StreamMixerAlsa::ResetTaskRunnerForTest() { | 239 void StreamMixerAlsa::ResetTaskRunnerForTest() { |
| 279 mixer_task_runner_ = base::ThreadTaskRunnerHandle::Get(); | 240 mixer_task_runner_ = base::ThreadTaskRunnerHandle::Get(); |
| 280 } | 241 } |
| 281 | 242 |
| 282 void StreamMixerAlsa::DefineAlsaParameters() { | 243 void StreamMixerAlsa::DefineAlsaParameters() { |
| 283 // Get the ALSA output configuration from the command line. | 244 // Get the ALSA output configuration from the command line. |
| 284 int buffer_size; | 245 alsa_buffer_size_ = GetSwitchValueNonNegativeInt( |
| 285 GetSwitchValueAsNonNegativeInt(switches::kAlsaOutputBufferSize, | 246 switches::kAlsaOutputBufferSize, kDefaultOutputBufferSizeFrames); |
| 286 kDefaultOutputBufferSizeFrames, &buffer_size); | |
| 287 alsa_buffer_size_ = buffer_size; | |
| 288 | 247 |
| 289 int period_size; | 248 alsa_period_size_ = GetSwitchValueNonNegativeInt( |
| 290 if (GetSwitchValueAsNonNegativeInt(switches::kAlsaOutputPeriodSize, | 249 switches::kAlsaOutputPeriodSize, alsa_buffer_size_ / 16); |
| 291 alsa_buffer_size_ / 16, &period_size)) { | 250 if (alsa_period_size_ >= alsa_buffer_size_) { |
| 292 if (period_size >= buffer_size) { | 251 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"; | 252 alsa_period_size_ = alsa_buffer_size_ / 2; |
| 294 period_size = buffer_size / 2; | |
| 295 } else { | |
| 296 alsa_period_explicitly_set = true; | |
| 297 } | |
| 298 } | 253 } |
| 299 alsa_period_size_ = period_size; | |
| 300 | 254 |
| 301 int start_threshold; | 255 alsa_start_threshold_ = GetSwitchValueNonNegativeInt( |
| 302 GetSwitchValueAsNonNegativeInt(switches::kAlsaOutputStartThreshold, | 256 switches::kAlsaOutputStartThreshold, |
| 303 (buffer_size / period_size) * period_size, | 257 (alsa_buffer_size_ / alsa_period_size_) * alsa_period_size_); |
| 304 &start_threshold); | 258 if (alsa_start_threshold_ > alsa_buffer_size_) { |
| 305 if (start_threshold > buffer_size) { | |
| 306 LOG(DFATAL) << "ALSA start threshold must be no larger than " | 259 LOG(DFATAL) << "ALSA start threshold must be no larger than " |
| 307 << "the buffer size"; | 260 << "the buffer size"; |
| 308 start_threshold = (buffer_size / period_size) * period_size; | 261 alsa_start_threshold_ = |
| 262 (alsa_buffer_size_ / alsa_period_size_) * alsa_period_size_; | |
| 309 } | 263 } |
| 310 alsa_start_threshold_ = start_threshold; | |
| 311 | 264 |
| 312 // By default, allow the transfer when at least period_size samples can be | 265 // By default, allow the transfer when at least period_size samples can be |
| 313 // processed. | 266 // processed. |
| 314 int avail_min; | 267 alsa_avail_min_ = GetSwitchValueNonNegativeInt(switches::kAlsaOutputAvailMin, |
| 315 GetSwitchValueAsNonNegativeInt(switches::kAlsaOutputAvailMin, period_size, | 268 alsa_period_size_); |
| 316 &avail_min); | 269 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"; | 270 LOG(DFATAL) << "ALSA avail min must be no larger than the buffer size"; |
| 319 avail_min = alsa_period_size_; | 271 alsa_avail_min_ = alsa_period_size_; |
| 320 } | 272 } |
| 321 alsa_avail_min_ = avail_min; | |
| 322 | 273 |
| 323 // --accept-resource-provider should imply a check close timeout of 0. | 274 // --accept-resource-provider should imply a check close timeout of 0. |
| 324 int default_close_timeout = chromecast::GetSwitchValueBoolean( | 275 int default_close_timeout = chromecast::GetSwitchValueBoolean( |
| 325 switches::kAcceptResourceProvider, false) | 276 switches::kAcceptResourceProvider, false) |
| 326 ? 0 | 277 ? 0 |
| 327 : kDefaultCheckCloseTimeoutMs; | 278 : kDefaultCheckCloseTimeoutMs; |
| 328 GetSwitchValueAsInt(switches::kAlsaCheckCloseTimeout, default_close_timeout, | 279 check_close_timeout_ = GetSwitchValueInt(switches::kAlsaCheckCloseTimeout, |
| 329 &check_close_timeout_); | 280 default_close_timeout); |
| 330 } | 281 } |
| 331 | 282 |
| 332 unsigned int StreamMixerAlsa::DetermineOutputRate(unsigned int requested_rate) { | 283 unsigned int StreamMixerAlsa::DetermineOutputRate(unsigned int requested_rate) { |
| 333 if (fixed_output_samples_per_second_ != kInvalidSampleRate) { | 284 if (fixed_output_samples_per_second_ != kInvalidSampleRate) { |
| 334 LOG(INFO) << "Requested output rate is " << requested_rate; | 285 LOG(INFO) << "Requested output rate is " << requested_rate; |
| 335 LOG(INFO) << "Cannot change rate since it is fixed to " | 286 LOG(INFO) << "Cannot change rate since it is fixed to " |
| 336 << fixed_output_samples_per_second_; | 287 << fixed_output_samples_per_second_; |
| 337 return fixed_output_samples_per_second_; | 288 return fixed_output_samples_per_second_; |
| 338 } | 289 } |
| 339 | 290 |
| (...skipping 95 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 435 snd_pcm_uframes_t requested_buffer_size = alsa_buffer_size_; | 386 snd_pcm_uframes_t requested_buffer_size = alsa_buffer_size_; |
| 436 RETURN_ERROR_CODE(PcmHwParamsSetBufferSizeNear, pcm_, pcm_hw_params_, | 387 RETURN_ERROR_CODE(PcmHwParamsSetBufferSizeNear, pcm_, pcm_hw_params_, |
| 437 &alsa_buffer_size_); | 388 &alsa_buffer_size_); |
| 438 if (requested_buffer_size != alsa_buffer_size_) { | 389 if (requested_buffer_size != alsa_buffer_size_) { |
| 439 LOG(WARNING) << "Requested buffer size (" << requested_buffer_size | 390 LOG(WARNING) << "Requested buffer size (" << requested_buffer_size |
| 440 << " frames) does not match the actual buffer size (" | 391 << " frames) does not match the actual buffer size (" |
| 441 << alsa_buffer_size_ | 392 << alsa_buffer_size_ |
| 442 << " frames). This may lead to an increase in " | 393 << " frames). This may lead to an increase in " |
| 443 "either audio latency or audio underruns."; | 394 "either audio latency or audio underruns."; |
| 444 | 395 |
| 445 // Always try to use the value for period_size that was passed in on the | 396 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; | 397 snd_pcm_uframes_t new_period_size = alsa_buffer_size_ / 2; |
| 451 LOG(DFATAL) << "Configured period size (" << alsa_period_size_ | 398 LOG(DFATAL) << "Configured period size (" << alsa_period_size_ |
| 452 << ") is >= actual buffer size (" << alsa_buffer_size_ | 399 << ") is >= actual buffer size (" << alsa_buffer_size_ |
| 453 << "); reducing to " << new_period_size; | 400 << "); reducing to " << new_period_size; |
| 454 alsa_period_size_ = new_period_size; | 401 alsa_period_size_ = new_period_size; |
| 455 } | 402 } |
| 456 // Scale the start threshold and avail_min based on the new buffer size. | 403 // Scale the start threshold and avail_min based on the new buffer size. |
| 457 float original_buffer_size = static_cast<float>(requested_buffer_size); | 404 float original_buffer_size = static_cast<float>(requested_buffer_size); |
| 458 float avail_min_ratio = original_buffer_size / alsa_avail_min_; | 405 float avail_min_ratio = original_buffer_size / alsa_avail_min_; |
| 459 alsa_avail_min_ = alsa_buffer_size_ / avail_min_ratio; | 406 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); | 600 DCHECK(input); |
| 654 | 601 |
| 655 // If the new input is a primary one, we may need to change the output | 602 // If the new input is a primary one, we may need to change the output |
| 656 // sample rate to match its input sample rate. | 603 // sample rate to match its input sample rate. |
| 657 // We only change the output rate if it is not set to a fixed value. | 604 // We only change the output rate if it is not set to a fixed value. |
| 658 if (input->primary() && | 605 if (input->primary() && |
| 659 fixed_output_samples_per_second_ == kInvalidSampleRate) { | 606 fixed_output_samples_per_second_ == kInvalidSampleRate) { |
| 660 CheckChangeOutputRate(input->input_samples_per_second()); | 607 CheckChangeOutputRate(input->input_samples_per_second()); |
| 661 } | 608 } |
| 662 | 609 |
| 610 auto type = input->content_type(); | |
| 611 if (input->primary()) { | |
| 612 input->SetContentTypeVolume(std::min(volume_limit_[type], volume_[type])); | |
| 613 } else { | |
| 614 input->SetContentTypeVolume(volume_[type]); | |
| 615 } | |
| 616 input->SetMuted(muted_[type]); | |
| 617 | |
| 663 check_close_timer_->Stop(); | 618 check_close_timer_->Stop(); |
| 664 switch (state_) { | 619 switch (state_) { |
| 665 case kStateUninitialized: | 620 case kStateUninitialized: |
| 666 requested_output_samples_per_second_ = input->input_samples_per_second(); | 621 requested_output_samples_per_second_ = input->input_samples_per_second(); |
| 667 Start(); | 622 Start(); |
| 668 // Fallthrough intended | 623 // Fallthrough intended |
| 669 case kStateNormalPlayback: { | 624 case kStateNormalPlayback: { |
| 670 bool found_filter_group = false; | 625 bool found_filter_group = false; |
| 671 input->Initialize(rendering_delay_); | 626 input->Initialize(rendering_delay_); |
| 672 for (auto&& filter_group : filter_groups_) { | 627 for (auto&& filter_group : filter_groups_) { |
| (...skipping 287 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 960 CastMediaShlib::LoopbackAudioObserver* observer) { | 915 CastMediaShlib::LoopbackAudioObserver* observer) { |
| 961 RUN_ON_MIXER_THREAD(&StreamMixerAlsa::RemoveLoopbackAudioObserver, observer); | 916 RUN_ON_MIXER_THREAD(&StreamMixerAlsa::RemoveLoopbackAudioObserver, observer); |
| 962 DCHECK(std::find(loopback_observers_.begin(), loopback_observers_.end(), | 917 DCHECK(std::find(loopback_observers_.begin(), loopback_observers_.end(), |
| 963 observer) != loopback_observers_.end()); | 918 observer) != loopback_observers_.end()); |
| 964 loopback_observers_.erase(std::remove(loopback_observers_.begin(), | 919 loopback_observers_.erase(std::remove(loopback_observers_.begin(), |
| 965 loopback_observers_.end(), observer), | 920 loopback_observers_.end(), observer), |
| 966 loopback_observers_.end()); | 921 loopback_observers_.end()); |
| 967 observer->OnRemoved(); | 922 observer->OnRemoved(); |
| 968 } | 923 } |
| 969 | 924 |
| 925 void StreamMixerAlsa::SetVolume(AudioContentType type, float level) { | |
| 926 RUN_ON_MIXER_THREAD(&StreamMixerAlsa::SetVolume, type, level); | |
| 927 volume_[type] = level; | |
| 928 float effective_volume = std::min(volume_limit_[type], level); | |
|
slan
2017/03/13 22:09:51
Regarding my earlier comment about using a VolumeC
kmackay
2017/03/14 00:20:59
Done.
| |
| 929 for (auto&& input : inputs_) { | |
| 930 if (input->content_type() == type) { | |
| 931 if (input->primary()) { | |
| 932 input->SetContentTypeVolume(effective_volume); | |
| 933 } else { | |
| 934 // Volume limits don't apply to effects streams. | |
| 935 input->SetContentTypeVolume(level); | |
| 936 } | |
| 937 } | |
| 938 } | |
| 939 | |
| 940 for (auto&& filter : filter_groups_) { | |
| 941 if (filter->content_type() == type) { | |
| 942 filter->set_volume(effective_volume); | |
| 943 } | |
| 944 } | |
| 945 } | |
| 946 | |
| 947 void StreamMixerAlsa::SetMuted(AudioContentType type, bool muted) { | |
| 948 RUN_ON_MIXER_THREAD(&StreamMixerAlsa::SetMuted, type, muted); | |
| 949 muted_[type] = muted; | |
| 950 for (auto&& input : inputs_) { | |
| 951 if (input->content_type() == type) { | |
| 952 input->SetMuted(muted); | |
| 953 } | |
| 954 } | |
| 955 } | |
| 956 | |
| 957 void StreamMixerAlsa::SetOutputLimit(AudioContentType type, float limit) { | |
| 958 RUN_ON_MIXER_THREAD(&StreamMixerAlsa::SetOutputLimit, type, limit); | |
| 959 LOG(INFO) << "Set volume limit for " << static_cast<int>(type) << " to " | |
| 960 << limit; | |
| 961 volume_limit_[type] = limit; | |
| 962 float effective_volume = std::min(volume_[type], limit); | |
| 963 for (auto&& input : inputs_) { | |
| 964 // Volume limits don't apply to effects streams. | |
| 965 if (input->primary() && input->content_type() == type) { | |
| 966 input->SetContentTypeVolume(effective_volume); | |
| 967 } | |
| 968 } | |
| 969 | |
| 970 for (auto&& filter : filter_groups_) { | |
| 971 if (filter->content_type() == type) { | |
| 972 filter->set_volume(effective_volume); | |
| 973 } | |
| 974 } | |
| 975 } | |
| 976 | |
| 970 } // namespace media | 977 } // namespace media |
| 971 } // namespace chromecast | 978 } // namespace chromecast |
| OLD | NEW |