| 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 |