| OLD | NEW |
| 1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2009 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 // THREAD SAFETY | 5 // THREAD SAFETY |
| 6 // | 6 // |
| 7 // The AlsaPcmOutputStream object's internal state is accessed by two threads: | 7 // The AlsaPcmOutputStream object's internal state is accessed by two threads: |
| 8 // | 8 // |
| 9 // client thread - creates the object and calls the public APIs. | 9 // client thread - creates the object and calls the public APIs. |
| 10 // message loop thread - executes all the internal tasks including querying | 10 // message loop thread - executes all the internal tasks including querying |
| (...skipping 84 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 95 | 95 |
| 96 // Set to 0 during debugging if you want error messages due to underrun | 96 // Set to 0 during debugging if you want error messages due to underrun |
| 97 // events or other recoverable errors. | 97 // events or other recoverable errors. |
| 98 #if defined(NDEBUG) | 98 #if defined(NDEBUG) |
| 99 static const int kPcmRecoverIsSilent = 1; | 99 static const int kPcmRecoverIsSilent = 1; |
| 100 #else | 100 #else |
| 101 static const int kPcmRecoverIsSilent = 0; | 101 static const int kPcmRecoverIsSilent = 0; |
| 102 #endif | 102 #endif |
| 103 | 103 |
| 104 const char AlsaPcmOutputStream::kDefaultDevice[] = "default"; | 104 const char AlsaPcmOutputStream::kDefaultDevice[] = "default"; |
| 105 const char AlsaPcmOutputStream::kAutoSelectDevice[] = ""; |
| 106 const char AlsaPcmOutputStream::kPlugPrefix[] = "plug:"; |
| 107 |
| 108 // Since we expect to only be able to wake up with a resolution of |
| 109 // kSleepErrorMilliseconds, double that for our minimum required latency. |
| 110 const int AlsaPcmOutputStream::kMinLatencyMicros = |
| 111 kSleepErrorMilliseconds * 2 * 1000; |
| 105 | 112 |
| 106 namespace { | 113 namespace { |
| 107 | 114 |
| 108 snd_pcm_format_t BitsToFormat(char bits_per_sample) { | 115 snd_pcm_format_t BitsToFormat(char bits_per_sample) { |
| 109 switch (bits_per_sample) { | 116 switch (bits_per_sample) { |
| 110 case 8: | 117 case 8: |
| 111 return SND_PCM_FORMAT_S8; | 118 return SND_PCM_FORMAT_U8; |
| 112 | 119 |
| 113 case 16: | 120 case 16: |
| 114 return SND_PCM_FORMAT_S16; | 121 return SND_PCM_FORMAT_S16; |
| 115 | 122 |
| 116 case 24: | 123 case 24: |
| 117 return SND_PCM_FORMAT_S24; | 124 return SND_PCM_FORMAT_S24; |
| 118 | 125 |
| 119 case 32: | 126 case 32: |
| 120 return SND_PCM_FORMAT_S32; | 127 return SND_PCM_FORMAT_S32; |
| 121 | 128 |
| 122 default: | 129 default: |
| 123 return SND_PCM_FORMAT_UNKNOWN; | 130 return SND_PCM_FORMAT_UNKNOWN; |
| 124 } | 131 } |
| 125 } | 132 } |
| 126 | 133 |
| 134 // While the "default" device may support multi-channel audio, in Alsa, only |
| 135 // the device names surround40, surround41, surround50, etc, have a defined |
| 136 // channel mapping according to Lennart: |
| 137 // |
| 138 // http://0pointer.de/blog/projects/guide-to-sound-apis.html |
| 139 // |
| 140 // This function makes a best guess at the specific > 2 channel device name |
| 141 // based on the number of channels requested. NULL is returned if no device |
| 142 // can be found to match the channel numbers. In this case, using |
| 143 // kDefaultDevice is probably the best bet. |
| 144 // |
| 145 // A five channel source is assumed to be surround50 instead of surround41 |
| 146 // (which is also 5 channels). |
| 147 // |
| 148 // TODO(ajwong): The source data should have enough info to tell us if we want |
| 149 // surround41 versus surround51, etc., instead of needing us to guess base don |
| 150 // channel number. Fix API to pass that data down. |
| 151 const char* GuessSpecificDeviceName(int channels) { |
| 152 switch (channels) { |
| 153 case 8: |
| 154 return "surround71"; |
| 155 |
| 156 case 7: |
| 157 return "surround70"; |
| 158 |
| 159 case 6: |
| 160 return "surround51"; |
| 161 |
| 162 case 5: |
| 163 return "surround50"; |
| 164 |
| 165 case 4: |
| 166 return "surround40"; |
| 167 |
| 168 default: |
| 169 return NULL; |
| 170 } |
| 171 } |
| 172 |
| 173 // Reorder PCM from AAC layout to Alsa layout. |
| 174 // TODO(fbarchard): Switch layout when ffmpeg is updated. |
| 175 template<class Format> |
| 176 static void Swizzle50Layout(Format* b, size_t filled) { |
| 177 static const int kNumSurroundChannels = 5; |
| 178 Format aac[kNumSurroundChannels]; |
| 179 for (size_t i = 0; i < filled; i += sizeof(aac), b += kNumSurroundChannels) { |
| 180 memcpy(aac, b, sizeof(aac)); |
| 181 b[0] = aac[1]; // L |
| 182 b[1] = aac[2]; // R |
| 183 b[2] = aac[3]; // Ls |
| 184 b[3] = aac[4]; // Rs |
| 185 b[4] = aac[0]; // C |
| 186 } |
| 187 } |
| 188 |
| 189 template<class Format> |
| 190 static void Swizzle51Layout(Format* b, size_t filled) { |
| 191 static const int kNumSurroundChannels = 6; |
| 192 Format aac[kNumSurroundChannels]; |
| 193 for (size_t i = 0; i < filled; i += sizeof(aac), b += kNumSurroundChannels) { |
| 194 memcpy(aac, b, sizeof(aac)); |
| 195 b[0] = aac[1]; // L |
| 196 b[1] = aac[2]; // R |
| 197 b[2] = aac[3]; // Ls |
| 198 b[3] = aac[4]; // Rs |
| 199 b[4] = aac[0]; // C |
| 200 b[5] = aac[5]; // LFE |
| 201 } |
| 202 } |
| 203 |
| 127 } // namespace | 204 } // namespace |
| 128 | 205 |
| 206 // Not in an anonymous so that it can be a friend to AlsaPcmOutputStream. |
| 129 std::ostream& operator<<(std::ostream& os, | 207 std::ostream& operator<<(std::ostream& os, |
| 130 AlsaPcmOutputStream::InternalState state) { | 208 AlsaPcmOutputStream::InternalState state) { |
| 131 switch (state) { | 209 switch (state) { |
| 132 case AlsaPcmOutputStream::kInError: | 210 case AlsaPcmOutputStream::kInError: |
| 133 os << "kInError"; | 211 os << "kInError"; |
| 134 break; | 212 break; |
| 135 case AlsaPcmOutputStream::kCreated: | 213 case AlsaPcmOutputStream::kCreated: |
| 136 os << "kCreated"; | 214 os << "kCreated"; |
| 137 break; | 215 break; |
| 138 case AlsaPcmOutputStream::kIsOpened: | 216 case AlsaPcmOutputStream::kIsOpened: |
| (...skipping 14 matching lines...) Expand all Loading... |
| 153 | 231 |
| 154 AlsaPcmOutputStream::AlsaPcmOutputStream(const std::string& device_name, | 232 AlsaPcmOutputStream::AlsaPcmOutputStream(const std::string& device_name, |
| 155 AudioManager::Format format, | 233 AudioManager::Format format, |
| 156 int channels, | 234 int channels, |
| 157 int sample_rate, | 235 int sample_rate, |
| 158 int bits_per_sample, | 236 int bits_per_sample, |
| 159 AlsaWrapper* wrapper, | 237 AlsaWrapper* wrapper, |
| 160 AudioManagerLinux* manager, | 238 AudioManagerLinux* manager, |
| 161 MessageLoop* message_loop) | 239 MessageLoop* message_loop) |
| 162 : shared_data_(MessageLoop::current()), | 240 : shared_data_(MessageLoop::current()), |
| 163 device_name_(device_name), | 241 requested_device_name_(device_name), |
| 164 pcm_format_(BitsToFormat(bits_per_sample)), | 242 pcm_format_(BitsToFormat(bits_per_sample)), |
| 165 channels_(channels), | 243 channels_(channels), |
| 166 sample_rate_(sample_rate), | 244 sample_rate_(sample_rate), |
| 167 bytes_per_sample_(bits_per_sample / 8), | 245 bytes_per_sample_(bits_per_sample / 8), |
| 168 bytes_per_frame_(channels_ * bits_per_sample / 8), | 246 bytes_per_frame_(channels_ * bits_per_sample / 8), |
| 247 should_downmix_(false), |
| 248 latency_micros_(0), |
| 249 micros_per_packet_(0), |
| 250 bytes_per_output_frame_(bytes_per_frame_), |
| 169 stop_stream_(false), | 251 stop_stream_(false), |
| 170 wrapper_(wrapper), | 252 wrapper_(wrapper), |
| 171 manager_(manager), | 253 manager_(manager), |
| 172 playback_handle_(NULL), | 254 playback_handle_(NULL), |
| 173 frames_per_packet_(0), | 255 frames_per_packet_(0), |
| 174 client_thread_loop_(MessageLoop::current()), | 256 client_thread_loop_(MessageLoop::current()), |
| 175 message_loop_(message_loop) { | 257 message_loop_(message_loop) { |
| 176 | 258 |
| 177 // Sanity check input values. | 259 // Sanity check input values. |
| 178 // | |
| 179 // TODO(scherkus): ALSA works fine if you pass in multichannel audio, however | |
| 180 // it seems to be mapped to the wrong channels. We may have to do some | |
| 181 // channel swizzling from decoder output to ALSA's preferred multichannel | |
| 182 // format. | |
| 183 if (channels_ != 1 && channels_ != 2) { | |
| 184 LOG(WARNING) << "Only 1 and 2 channel audio is supported right now."; | |
| 185 shared_data_.TransitionTo(kInError); | |
| 186 } | |
| 187 | |
| 188 if (AudioManager::AUDIO_PCM_LINEAR != format) { | 260 if (AudioManager::AUDIO_PCM_LINEAR != format) { |
| 189 LOG(WARNING) << "Only linear PCM supported."; | 261 LOG(WARNING) << "Only linear PCM supported."; |
| 190 shared_data_.TransitionTo(kInError); | 262 shared_data_.TransitionTo(kInError); |
| 191 } | 263 } |
| 192 | 264 |
| 193 if (pcm_format_ == SND_PCM_FORMAT_UNKNOWN) { | 265 if (pcm_format_ == SND_PCM_FORMAT_UNKNOWN) { |
| 194 LOG(WARNING) << "Unsupported bits per sample: " << bits_per_sample; | 266 LOG(WARNING) << "Unsupported bits per sample: " << bits_per_sample; |
| 195 shared_data_.TransitionTo(kInError); | 267 shared_data_.TransitionTo(kInError); |
| 196 } | 268 } |
| 197 } | 269 } |
| (...skipping 17 matching lines...) Expand all Loading... |
| 215 | 287 |
| 216 if (shared_data_.state() == kInError) { | 288 if (shared_data_.state() == kInError) { |
| 217 return false; | 289 return false; |
| 218 } | 290 } |
| 219 | 291 |
| 220 if (!shared_data_.CanTransitionTo(kIsOpened)) { | 292 if (!shared_data_.CanTransitionTo(kIsOpened)) { |
| 221 NOTREACHED() << "Invalid state: " << shared_data_.state(); | 293 NOTREACHED() << "Invalid state: " << shared_data_.state(); |
| 222 return false; | 294 return false; |
| 223 } | 295 } |
| 224 | 296 |
| 225 // Try to open the device. | |
| 226 snd_pcm_t* handle = NULL; | |
| 227 int error = wrapper_->PcmOpen(&handle, device_name_.c_str(), | |
| 228 SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK); | |
| 229 if (error < 0) { | |
| 230 LOG(ERROR) << "Cannot open audio device (" << device_name_ << "): " | |
| 231 << wrapper_->StrError(error); | |
| 232 return false; | |
| 233 } | |
| 234 | |
| 235 // Configure the device for software resampling, and add enough buffer for | |
| 236 // two audio packets. | |
| 237 int micros_per_packet = | |
| 238 FramesToMicros(packet_size / bytes_per_frame_, sample_rate_); | |
| 239 if ((error = wrapper_->PcmSetParams(handle, | |
| 240 pcm_format_, | |
| 241 SND_PCM_ACCESS_RW_INTERLEAVED, | |
| 242 channels_, | |
| 243 sample_rate_, | |
| 244 1, // soft_resample -- let ALSA resample | |
| 245 micros_per_packet * 2)) < 0) { | |
| 246 LOG(ERROR) << "Unable to set PCM parameters for (" << device_name_ | |
| 247 << "): " << wrapper_->StrError(error); | |
| 248 if (!CloseDevice(handle)) { | |
| 249 // TODO(ajwong): Retry on certain errors? | |
| 250 LOG(WARNING) << "Unable to close audio device. Leaking handle."; | |
| 251 } | |
| 252 return false; | |
| 253 } | |
| 254 | |
| 255 // We do not need to check if the transition was successful because | 297 // We do not need to check if the transition was successful because |
| 256 // CanTransitionTo() was checked above, and it is assumed that this | 298 // CanTransitionTo() was checked above, and it is assumed that this |
| 257 // object's public API is only called on one thread so the state cannot | 299 // object's public API is only called on one thread so the state cannot |
| 258 // transition out from under us. | 300 // transition out from under us. |
| 259 shared_data_.TransitionTo(kIsOpened); | 301 shared_data_.TransitionTo(kIsOpened); |
| 260 message_loop_->PostTask( | 302 message_loop_->PostTask( |
| 261 FROM_HERE, | 303 FROM_HERE, |
| 262 NewRunnableMethod(this, &AlsaPcmOutputStream::FinishOpen, | 304 NewRunnableMethod(this, &AlsaPcmOutputStream::OpenTask, packet_size)); |
| 263 handle, packet_size)); | |
| 264 | 305 |
| 265 return true; | 306 return true; |
| 266 } | 307 } |
| 267 | 308 |
| 268 void AlsaPcmOutputStream::Close() { | 309 void AlsaPcmOutputStream::Close() { |
| 269 DCHECK_EQ(MessageLoop::current(), client_thread_loop_); | 310 DCHECK_EQ(MessageLoop::current(), client_thread_loop_); |
| 270 | 311 |
| 271 // Sanity check that the transition occurs correctly. It is safe to | 312 // Sanity check that the transition occurs correctly. It is safe to |
| 272 // continue anyways because all operations for closing are idempotent. | 313 // continue anyways because all operations for closing are idempotent. |
| 273 if (shared_data_.TransitionTo(kIsClosed) != kIsClosed) { | 314 if (shared_data_.TransitionTo(kIsClosed) != kIsClosed) { |
| (...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 314 | 355 |
| 315 shared_data_.set_volume(static_cast<float>(left_level)); | 356 shared_data_.set_volume(static_cast<float>(left_level)); |
| 316 } | 357 } |
| 317 | 358 |
| 318 void AlsaPcmOutputStream::GetVolume(double* left_level, double* right_level) { | 359 void AlsaPcmOutputStream::GetVolume(double* left_level, double* right_level) { |
| 319 DCHECK_EQ(MessageLoop::current(), client_thread_loop_); | 360 DCHECK_EQ(MessageLoop::current(), client_thread_loop_); |
| 320 | 361 |
| 321 *left_level = *right_level = shared_data_.volume(); | 362 *left_level = *right_level = shared_data_.volume(); |
| 322 } | 363 } |
| 323 | 364 |
| 324 void AlsaPcmOutputStream::FinishOpen(snd_pcm_t* playback_handle, | 365 void AlsaPcmOutputStream::OpenTask(size_t packet_size) { |
| 325 size_t packet_size) { | |
| 326 DCHECK_EQ(MessageLoop::current(), message_loop_); | 366 DCHECK_EQ(MessageLoop::current(), message_loop_); |
| 327 | 367 |
| 328 playback_handle_ = playback_handle; | 368 // Initialize the configuration variables. |
| 329 packet_.reset(new Packet(packet_size)); | |
| 330 frames_per_packet_ = packet_size / bytes_per_frame_; | 369 frames_per_packet_ = packet_size / bytes_per_frame_; |
| 370 |
| 371 // Try to open the device. |
| 372 micros_per_packet_ = |
| 373 FramesToMicros(packet_size / bytes_per_frame_, sample_rate_); |
| 374 latency_micros_ = std::max(AlsaPcmOutputStream::kMinLatencyMicros, |
| 375 micros_per_packet_ * 2); |
| 376 if (requested_device_name_ == kAutoSelectDevice) { |
| 377 playback_handle_ = AutoSelectDevice(latency_micros_); |
| 378 if (playback_handle_) { |
| 379 LOG(INFO) << "Auto-selected device: " << device_name_; |
| 380 } |
| 381 } else { |
| 382 device_name_ = requested_device_name_; |
| 383 playback_handle_ = OpenDevice(device_name_, channels_, latency_micros_); |
| 384 } |
| 385 |
| 386 // Finish initializing the stream if the device was opened successfully. |
| 387 if (playback_handle_ == NULL) { |
| 388 stop_stream_ = true; |
| 389 } else { |
| 390 packet_.reset(new Packet(packet_size)); |
| 391 if (should_downmix_) { |
| 392 bytes_per_output_frame_ = 2 * bytes_per_sample_; |
| 393 } |
| 394 } |
| 331 } | 395 } |
| 332 | 396 |
| 333 void AlsaPcmOutputStream::StartTask() { | 397 void AlsaPcmOutputStream::StartTask() { |
| 334 DCHECK_EQ(MessageLoop::current(), message_loop_); | 398 DCHECK_EQ(MessageLoop::current(), message_loop_); |
| 335 | 399 |
| 400 if (stop_stream_) { |
| 401 return; |
| 402 } |
| 403 |
| 336 // When starting again, drop all packets in the device and prepare it again | 404 // When starting again, drop all packets in the device and prepare it again |
| 337 // incase we are restarting from a pause state and need to flush old data. | 405 // incase we are restarting from a pause state and need to flush old data. |
| 338 int error = wrapper_->PcmDrop(playback_handle_); | 406 int error = wrapper_->PcmDrop(playback_handle_); |
| 339 if (error < 0 && error != -EAGAIN) { | 407 if (error < 0 && error != -EAGAIN) { |
| 340 LOG(ERROR) << "Failure clearing playback device (" | 408 LOG(ERROR) << "Failure clearing playback device (" |
| 341 << wrapper_->PcmName(playback_handle_) << "): " | 409 << wrapper_->PcmName(playback_handle_) << "): " |
| 342 << wrapper_->StrError(error); | 410 << wrapper_->StrError(error); |
| 343 stop_stream_ = true; | 411 stop_stream_ = true; |
| 344 return; | 412 return; |
| 345 } | 413 } |
| 346 | 414 |
| 347 error = wrapper_->PcmPrepare(playback_handle_); | 415 error = wrapper_->PcmPrepare(playback_handle_); |
| 348 if (error < 0 && error != -EAGAIN) { | 416 if (error < 0 && error != -EAGAIN) { |
| 349 LOG(ERROR) << "Failure preparing stream (" | 417 LOG(ERROR) << "Failure preparing stream (" |
| 350 << wrapper_->PcmName(playback_handle_) << "): " | 418 << wrapper_->PcmName(playback_handle_) << "): " |
| 351 << wrapper_->StrError(error); | 419 << wrapper_->StrError(error); |
| 352 stop_stream_ = true; | 420 stop_stream_ = true; |
| 353 return; | 421 return; |
| 354 } | 422 } |
| 355 | 423 |
| 356 // Do a best-effort write of 2 packets to pre-roll. | 424 // Do a best-effort pre-roll to fill the buffer. Use integer rounding to find |
| 425 // the maximum number of full packets that can fit into the buffer. |
| 357 // | 426 // |
| 358 // TODO(ajwong): Make this track with the us_latency set in Open(). | 427 // TODO(ajwong): Handle EAGAIN. |
| 359 // Also handle EAGAIN. | 428 const int num_preroll = latency_micros_ / micros_per_packet_; |
| 360 BufferPacket(packet_.get()); | 429 for (int i = 0; i < num_preroll; ++i) { |
| 361 WritePacket(packet_.get()); | 430 BufferPacket(packet_.get()); |
| 362 BufferPacket(packet_.get()); | 431 WritePacket(packet_.get()); |
| 363 WritePacket(packet_.get()); | 432 } |
| 364 | 433 |
| 365 ScheduleNextWrite(packet_.get()); | 434 ScheduleNextWrite(packet_.get()); |
| 366 } | 435 } |
| 367 | 436 |
| 368 void AlsaPcmOutputStream::CloseTask() { | 437 void AlsaPcmOutputStream::CloseTask() { |
| 369 // NOTE: Keep this function idempotent to handle errors that might cause | 438 // NOTE: Keep this function idempotent to handle errors that might cause |
| 370 // multiple CloseTasks to be posted. | 439 // multiple CloseTasks to be posted. |
| 371 DCHECK_EQ(MessageLoop::current(), message_loop_); | 440 DCHECK_EQ(MessageLoop::current(), message_loop_); |
| 372 | 441 |
| 373 // Shutdown the audio device. | 442 // Shutdown the audio device. |
| (...skipping 29 matching lines...) Expand all Loading... |
| 403 error, | 472 error, |
| 404 kPcmRecoverIsSilent); | 473 kPcmRecoverIsSilent); |
| 405 if (error < 0) { | 474 if (error < 0) { |
| 406 LOG(ERROR) << "Failed querying delay: " << wrapper_->StrError(error); | 475 LOG(ERROR) << "Failed querying delay: " << wrapper_->StrError(error); |
| 407 } | 476 } |
| 408 | 477 |
| 409 // TODO(hclam): If we cannot query the delay, we may want to stop | 478 // TODO(hclam): If we cannot query the delay, we may want to stop |
| 410 // the playback and report an error. | 479 // the playback and report an error. |
| 411 delay = 0; | 480 delay = 0; |
| 412 } else { | 481 } else { |
| 413 delay *= bytes_per_frame_; | 482 delay *= bytes_per_output_frame_; |
| 414 } | 483 } |
| 415 | 484 |
| 416 packet->used = 0; | 485 packet->used = 0; |
| 417 packet->size = shared_data_.OnMoreData(this, packet->buffer.get(), | 486 packet->size = shared_data_.OnMoreData(this, packet->buffer.get(), |
| 418 packet->capacity, delay); | 487 packet->capacity, delay); |
| 419 CHECK(packet->size <= packet->capacity) << "Data source overran buffer."; | 488 CHECK(packet->size <= packet->capacity) << "Data source overran buffer."; |
| 420 | 489 |
| 421 // This should not happen, but incase it does, drop any trailing bytes | 490 // This should not happen, but incase it does, drop any trailing bytes |
| 422 // that aren't large enough to make a frame. Without this, packet writing | 491 // that aren't large enough to make a frame. Without this, packet writing |
| 423 // may stall because the last few bytes in the packet may never get used by | 492 // may stall because the last few bytes in the packet may never get used by |
| 424 // WritePacket. | 493 // WritePacket. |
| 425 DCHECK(packet->size % bytes_per_frame_ == 0); | 494 DCHECK(packet->size % bytes_per_frame_ == 0); |
| 426 packet->size = (packet->size / bytes_per_frame_) * bytes_per_frame_; | 495 packet->size = (packet->size / bytes_per_frame_) * bytes_per_frame_; |
| 427 | 496 |
| 428 media::AdjustVolume(packet->buffer.get(), | 497 if (should_downmix_) { |
| 429 packet->size, | 498 if (media::FoldChannels(packet->buffer.get(), |
| 430 channels_, | 499 packet->size, |
| 431 bytes_per_sample_, | 500 channels_, |
| 432 shared_data_.volume()); | 501 bytes_per_sample_, |
| 502 shared_data_.volume())) { |
| 503 // Adjust packet size for downmix. |
| 504 packet->size = |
| 505 packet->size / bytes_per_frame_ * bytes_per_output_frame_; |
| 506 } else { |
| 507 LOG(ERROR) << "Folding failed"; |
| 508 } |
| 509 } else { |
| 510 // TODO(ajwong): Handle other channel orderings. |
| 511 |
| 512 // Handle channel order for 5.0 audio. |
| 513 if (channels_ == 5) { |
| 514 if (bytes_per_sample_ == 1) { |
| 515 Swizzle50Layout(reinterpret_cast<uint8*>(packet->buffer.get()), |
| 516 packet->size); |
| 517 } else if (bytes_per_sample_ == 2) { |
| 518 Swizzle50Layout(reinterpret_cast<int16*>(packet->buffer.get()), |
| 519 packet->size); |
| 520 } else if (bytes_per_sample_ == 4) { |
| 521 Swizzle50Layout(reinterpret_cast<int32*>(packet->buffer.get()), |
| 522 packet->size); |
| 523 } |
| 524 } |
| 525 |
| 526 // Handle channel order for 5.1 audio. |
| 527 if (channels_ == 6) { |
| 528 if (bytes_per_sample_ == 1) { |
| 529 Swizzle51Layout(reinterpret_cast<uint8*>(packet->buffer.get()), |
| 530 packet->size); |
| 531 } else if (bytes_per_sample_ == 2) { |
| 532 Swizzle51Layout(reinterpret_cast<int16*>(packet->buffer.get()), |
| 533 packet->size); |
| 534 } else if (bytes_per_sample_ == 4) { |
| 535 Swizzle51Layout(reinterpret_cast<int32*>(packet->buffer.get()), |
| 536 packet->size); |
| 537 } |
| 538 } |
| 539 |
| 540 media::AdjustVolume(packet->buffer.get(), |
| 541 packet->size, |
| 542 channels_, |
| 543 bytes_per_sample_, |
| 544 shared_data_.volume()); |
| 545 } |
| 433 } | 546 } |
| 434 } | 547 } |
| 435 | 548 |
| 436 void AlsaPcmOutputStream::WritePacket(Packet* packet) { | 549 void AlsaPcmOutputStream::WritePacket(Packet* packet) { |
| 437 DCHECK_EQ(MessageLoop::current(), message_loop_); | 550 DCHECK_EQ(MessageLoop::current(), message_loop_); |
| 438 | 551 |
| 439 CHECK(packet->size % bytes_per_frame_ == 0); | 552 CHECK(packet->size % bytes_per_output_frame_ == 0); |
| 440 | 553 |
| 441 // If the device is in error, just eat the bytes. | 554 // If the device is in error, just eat the bytes. |
| 442 if (stop_stream_) { | 555 if (stop_stream_) { |
| 443 packet->used = packet->size; | 556 packet->used = packet->size; |
| 444 return; | 557 return; |
| 445 } | 558 } |
| 446 | 559 |
| 447 if (packet->used < packet->size) { | 560 if (packet->used < packet->size) { |
| 448 char* buffer_pos = packet->buffer.get() + packet->used; | 561 char* buffer_pos = packet->buffer.get() + packet->used; |
| 449 snd_pcm_sframes_t frames = FramesInPacket(*packet, bytes_per_frame_); | 562 snd_pcm_sframes_t frames = FramesInPacket(*packet, bytes_per_output_frame_); |
| 450 | 563 |
| 451 DCHECK_GT(frames, 0); | 564 DCHECK_GT(frames, 0); |
| 452 | 565 |
| 453 snd_pcm_sframes_t frames_written = | 566 snd_pcm_sframes_t frames_written = |
| 454 wrapper_->PcmWritei(playback_handle_, buffer_pos, frames); | 567 wrapper_->PcmWritei(playback_handle_, buffer_pos, frames); |
| 455 if (frames_written < 0) { | 568 if (frames_written < 0) { |
| 456 // Attempt once to immediately recover from EINTR, | 569 // Attempt once to immediately recover from EINTR, |
| 457 // EPIPE (overrun/underrun), ESTRPIPE (stream suspended). WritePacket | 570 // EPIPE (overrun/underrun), ESTRPIPE (stream suspended). WritePacket |
| 458 // will eventually be called again, so eventual recovery will happen if | 571 // will eventually be called again, so eventual recovery will happen if |
| 459 // muliple retries are required. | 572 // muliple retries are required. |
| 460 frames_written = wrapper_->PcmRecover(playback_handle_, | 573 frames_written = wrapper_->PcmRecover(playback_handle_, |
| 461 frames_written, | 574 frames_written, |
| 462 kPcmRecoverIsSilent); | 575 kPcmRecoverIsSilent); |
| 463 } | 576 } |
| 464 | 577 |
| 465 if (frames_written < 0) { | 578 if (frames_written < 0) { |
| 466 // TODO(ajwong): Is EAGAIN the only error we want to except from stopping | 579 // TODO(ajwong): Is EAGAIN the only error we want to except from stopping |
| 467 // the pcm playback? | 580 // the pcm playback? |
| 468 if (frames_written != -EAGAIN) { | 581 if (frames_written != -EAGAIN) { |
| 469 LOG(ERROR) << "Failed to write to pcm device: " | 582 LOG(ERROR) << "Failed to write to pcm device: " |
| 470 << wrapper_->StrError(frames_written); | 583 << wrapper_->StrError(frames_written); |
| 471 shared_data_.OnError(this, frames_written); | 584 shared_data_.OnError(this, frames_written); |
| 472 stop_stream_ = true; | 585 stop_stream_ = true; |
| 473 } | 586 } |
| 474 } else { | 587 } else { |
| 475 packet->used += frames_written * bytes_per_frame_; | 588 packet->used += frames_written * bytes_per_output_frame_; |
| 476 } | 589 } |
| 477 } | 590 } |
| 478 } | 591 } |
| 479 | 592 |
| 480 void AlsaPcmOutputStream::WriteTask() { | 593 void AlsaPcmOutputStream::WriteTask() { |
| 481 DCHECK_EQ(MessageLoop::current(), message_loop_); | 594 DCHECK_EQ(MessageLoop::current(), message_loop_); |
| 482 | 595 |
| 483 if (stop_stream_) { | 596 if (stop_stream_) { |
| 484 return; | 597 return; |
| 485 } | 598 } |
| 486 | 599 |
| 487 BufferPacket(packet_.get()); | 600 BufferPacket(packet_.get()); |
| 488 WritePacket(packet_.get()); | 601 WritePacket(packet_.get()); |
| 489 | 602 |
| 490 ScheduleNextWrite(packet_.get()); | 603 ScheduleNextWrite(packet_.get()); |
| 491 } | 604 } |
| 492 | 605 |
| 493 void AlsaPcmOutputStream::ScheduleNextWrite(Packet* current_packet) { | 606 void AlsaPcmOutputStream::ScheduleNextWrite(Packet* current_packet) { |
| 494 DCHECK_EQ(MessageLoop::current(), message_loop_); | 607 DCHECK_EQ(MessageLoop::current(), message_loop_); |
| 495 | 608 |
| 496 if (stop_stream_) { | 609 if (stop_stream_) { |
| 497 return; | 610 return; |
| 498 } | 611 } |
| 499 | 612 |
| 500 // Calculate when we should have enough buffer for another packet of data. | 613 // Calculate when we should have enough buffer for another packet of data. |
| 501 int frames_leftover = FramesInPacket(*current_packet, bytes_per_frame_); | 614 // Make sure to take into consideration down-mixing. |
| 502 int frames_needed = | 615 int frames_leftover = |
| 503 frames_leftover > 0 ? frames_leftover : frames_per_packet_; | 616 FramesInPacket(*current_packet, bytes_per_output_frame_); |
| 504 int frames_until_empty_enough = frames_needed - GetAvailableFrames(); | 617 int frames_avail_wanted = |
| 618 (frames_leftover > 0) ? frames_leftover : frames_per_packet_; |
| 619 int frames_until_empty_enough = frames_avail_wanted - GetAvailableFrames(); |
| 505 int next_fill_time_ms = | 620 int next_fill_time_ms = |
| 506 FramesToMillis(frames_until_empty_enough, sample_rate_); | 621 FramesToMillis(frames_until_empty_enough, sample_rate_); |
| 507 | 622 |
| 508 // Adjust for timer resolution issues. | 623 // Adjust for timer resolution issues. |
| 509 if (next_fill_time_ms > kSleepErrorMilliseconds) { | 624 if (next_fill_time_ms > kSleepErrorMilliseconds) { |
| 510 next_fill_time_ms -= kSleepErrorMilliseconds; | 625 next_fill_time_ms -= kSleepErrorMilliseconds; |
| 511 } | 626 } |
| 512 | 627 |
| 513 // Avoid busy looping if the data source is exhausted. | 628 // Avoid busy looping if the data source is exhausted. |
| 514 if (current_packet->size == 0) { | 629 if (current_packet->size == 0) { |
| (...skipping 23 matching lines...) Expand all Loading... |
| 538 } | 653 } |
| 539 | 654 |
| 540 int64 AlsaPcmOutputStream::FramesToMicros(int frames, int sample_rate) { | 655 int64 AlsaPcmOutputStream::FramesToMicros(int frames, int sample_rate) { |
| 541 return frames * base::Time::kMicrosecondsPerSecond / sample_rate; | 656 return frames * base::Time::kMicrosecondsPerSecond / sample_rate; |
| 542 } | 657 } |
| 543 | 658 |
| 544 int64 AlsaPcmOutputStream::FramesToMillis(int frames, int sample_rate) { | 659 int64 AlsaPcmOutputStream::FramesToMillis(int frames, int sample_rate) { |
| 545 return frames * base::Time::kMillisecondsPerSecond / sample_rate; | 660 return frames * base::Time::kMillisecondsPerSecond / sample_rate; |
| 546 } | 661 } |
| 547 | 662 |
| 663 std::string AlsaPcmOutputStream::FindDeviceForChannels(int channels) { |
| 664 // Constants specified by the ALSA API for device hints. |
| 665 static const int kGetAllDevices = -1; |
| 666 static const char kPcmInterfaceName[] = "pcm"; |
| 667 static const char kIoHintName[] = "IOID"; |
| 668 static const char kNameHintName[] = "NAME"; |
| 669 |
| 670 const char* wanted_device = GuessSpecificDeviceName(channels); |
| 671 if (!wanted_device) { |
| 672 return ""; |
| 673 } |
| 674 |
| 675 std::string guessed_device; |
| 676 void** hints = NULL; |
| 677 int error = wrapper_->DeviceNameHint(kGetAllDevices, |
| 678 kPcmInterfaceName, |
| 679 &hints); |
| 680 if (error == 0) { |
| 681 // NOTE: Do not early return from inside this if statement. The |
| 682 // hints above need to be freed. |
| 683 for (void** hint_iter = hints; *hint_iter != NULL; hint_iter++) { |
| 684 // Only examine devices that are output capable.. Valid values are |
| 685 // "Input", "Output", and NULL which means both input and output. |
| 686 scoped_ptr_malloc<char> io( |
| 687 wrapper_->DeviceNameGetHint(*hint_iter, kIoHintName)); |
| 688 if (io != NULL && strcmp(io.get(), "Input") == 0) |
| 689 continue; |
| 690 |
| 691 // Attempt to select the closest device for number of channels. |
| 692 scoped_ptr_malloc<char> name( |
| 693 wrapper_->DeviceNameGetHint(*hint_iter, kNameHintName)); |
| 694 if (strncmp(wanted_device, name.get(), strlen(wanted_device)) == 0) { |
| 695 guessed_device = name.get(); |
| 696 break; |
| 697 } |
| 698 } |
| 699 |
| 700 // Destory the hint now that we're done with it. |
| 701 wrapper_->DeviceNameFreeHint(hints); |
| 702 hints = NULL; |
| 703 } else { |
| 704 LOG(ERROR) << "Unable to get hints for devices: " |
| 705 << wrapper_->StrError(error); |
| 706 } |
| 707 |
| 708 return guessed_device; |
| 709 } |
| 710 |
| 711 snd_pcm_t* AlsaPcmOutputStream::OpenDevice(const std::string& device_name, |
| 712 int channels, |
| 713 unsigned int latency) { |
| 714 snd_pcm_t* handle = NULL; |
| 715 int error = wrapper_->PcmOpen(&handle, device_name.c_str(), |
| 716 SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK); |
| 717 if (error < 0) { |
| 718 LOG(ERROR) << "Cannot open audio device (" << device_name << "): " |
| 719 << wrapper_->StrError(error); |
| 720 return NULL; |
| 721 } |
| 722 |
| 723 // Configure the device for software resampling. |
| 724 if ((error = wrapper_->PcmSetParams(handle, |
| 725 pcm_format_, |
| 726 SND_PCM_ACCESS_RW_INTERLEAVED, |
| 727 channels, |
| 728 sample_rate_, |
| 729 1, // soft_resample -- let ALSA resample |
| 730 latency)) < 0) { |
| 731 LOG(ERROR) << "Unable to set PCM parameters for (" << device_name |
| 732 << "): " << wrapper_->StrError(error) |
| 733 << " -- Format: " << pcm_format_ |
| 734 << " Channels: " << channels |
| 735 << " Latency (us): " << latency; |
| 736 if (!CloseDevice(handle)) { |
| 737 // TODO(ajwong): Retry on certain errors? |
| 738 LOG(WARNING) << "Unable to close audio device. Leaking handle."; |
| 739 } |
| 740 return NULL; |
| 741 } |
| 742 |
| 743 return handle; |
| 744 } |
| 745 |
| 548 bool AlsaPcmOutputStream::CloseDevice(snd_pcm_t* handle) { | 746 bool AlsaPcmOutputStream::CloseDevice(snd_pcm_t* handle) { |
| 549 int error = wrapper_->PcmClose(handle); | 747 int error = wrapper_->PcmClose(handle); |
| 550 if (error < 0) { | 748 if (error < 0) { |
| 551 LOG(ERROR) << "Cannot close audio device (" << wrapper_->PcmName(handle) | 749 LOG(ERROR) << "Cannot close audio device (" << wrapper_->PcmName(handle) |
| 552 << "): " << wrapper_->StrError(error); | 750 << "): " << wrapper_->StrError(error); |
| 553 return false; | 751 return false; |
| 554 } | 752 } |
| 555 | 753 |
| 556 return true; | 754 return true; |
| 557 } | 755 } |
| (...skipping 15 matching lines...) Expand all Loading... |
| 573 } | 771 } |
| 574 if (available_frames < 0) { | 772 if (available_frames < 0) { |
| 575 LOG(ERROR) << "Failed querying available frames. Assuming 0: " | 773 LOG(ERROR) << "Failed querying available frames. Assuming 0: " |
| 576 << wrapper_->StrError(available_frames); | 774 << wrapper_->StrError(available_frames); |
| 577 return 0; | 775 return 0; |
| 578 } | 776 } |
| 579 | 777 |
| 580 return available_frames; | 778 return available_frames; |
| 581 } | 779 } |
| 582 | 780 |
| 781 snd_pcm_t* AlsaPcmOutputStream::AutoSelectDevice(unsigned int latency) { |
| 782 // For auto-selection: |
| 783 // 1) Attempt to open a device that best matches the number of channels |
| 784 // requested. |
| 785 // 2) If that fails, attempt the "plug:" version of it incase ALSA can |
| 786 // remap do some software conversion to make it work. |
| 787 // 3) Fallback to kDefaultDevice. |
| 788 // 4) If that fails too, try the "plug:" version of kDefaultDevice. |
| 789 // 5) Give up. |
| 790 snd_pcm_t* handle = NULL; |
| 791 device_name_ = FindDeviceForChannels(channels_); |
| 792 |
| 793 // Step 1. |
| 794 if (!device_name_.empty()) { |
| 795 if ((handle = OpenDevice(device_name_, channels_, latency)) != NULL) { |
| 796 return handle; |
| 797 } |
| 798 |
| 799 // Step 2. |
| 800 device_name_ = kPlugPrefix + device_name_; |
| 801 if ((handle = OpenDevice(device_name_, channels_, latency)) != NULL) { |
| 802 return handle; |
| 803 } |
| 804 } |
| 805 |
| 806 // For the kDefaultDevice device, we can only reliably depend on 2-channel |
| 807 // output to have the correct ordering according to Lennart. For the channel |
| 808 // formats that we know how to downmix from (5 channel to 6 channel), setup |
| 809 // downmixing. |
| 810 // |
| 811 // TODO(ajwong): We need a SupportsFolding() function. |
| 812 int default_channels = channels_; |
| 813 if (default_channels >= 5 && default_channels <= 6) { |
| 814 should_downmix_ = true; |
| 815 default_channels = 2; |
| 816 } |
| 817 |
| 818 // Step 3. |
| 819 device_name_ = kDefaultDevice; |
| 820 if ((handle = OpenDevice(device_name_, default_channels, latency)) != NULL) { |
| 821 return handle; |
| 822 } |
| 823 |
| 824 // Step 4. |
| 825 device_name_ = kPlugPrefix + device_name_; |
| 826 if ((handle = OpenDevice(device_name_, default_channels, latency)) != NULL) { |
| 827 return handle; |
| 828 } |
| 829 |
| 830 // Unable to open any device. |
| 831 device_name_.clear(); |
| 832 return NULL; |
| 833 } |
| 834 |
| 583 AudioManagerLinux* AlsaPcmOutputStream::manager() { | 835 AudioManagerLinux* AlsaPcmOutputStream::manager() { |
| 584 DCHECK_EQ(MessageLoop::current(), client_thread_loop_); | 836 DCHECK_EQ(MessageLoop::current(), client_thread_loop_); |
| 585 return manager_; | 837 return manager_; |
| 586 } | 838 } |
| 587 | 839 |
| 588 AlsaPcmOutputStream::SharedData::SharedData( | 840 AlsaPcmOutputStream::SharedData::SharedData( |
| 589 MessageLoop* state_transition_loop) | 841 MessageLoop* state_transition_loop) |
| 590 : state_(kCreated), | 842 : state_(kCreated), |
| 591 volume_(1.0f), | 843 volume_(1.0f), |
| 592 source_callback_(NULL), | 844 source_callback_(NULL), |
| (...skipping 91 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 684 } | 936 } |
| 685 | 937 |
| 686 // Changes the AudioSourceCallback to proxy calls to. Pass in NULL to | 938 // Changes the AudioSourceCallback to proxy calls to. Pass in NULL to |
| 687 // release ownership of the currently registered callback. | 939 // release ownership of the currently registered callback. |
| 688 void AlsaPcmOutputStream::SharedData::set_source_callback( | 940 void AlsaPcmOutputStream::SharedData::set_source_callback( |
| 689 AudioSourceCallback* callback) { | 941 AudioSourceCallback* callback) { |
| 690 DCHECK_EQ(MessageLoop::current(), state_transition_loop_); | 942 DCHECK_EQ(MessageLoop::current(), state_transition_loop_); |
| 691 AutoLock l(lock_); | 943 AutoLock l(lock_); |
| 692 source_callback_ = callback; | 944 source_callback_ = callback; |
| 693 } | 945 } |
| OLD | NEW |