| OLD | NEW |
| 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 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 "media/audio/mac/audio_output_mac.h" | 5 #include "media/audio/mac/audio_output_mac.h" |
| 6 | 6 |
| 7 #include "base/basictypes.h" | 7 #include "base/basictypes.h" |
| 8 #include "base/debug/trace_event.h" | 8 #include "base/debug/trace_event.h" |
| 9 #include "base/logging.h" | 9 #include "base/logging.h" |
| 10 #include "base/memory/scoped_ptr.h" | 10 #include "base/memory/scoped_ptr.h" |
| (...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 84 silence_bytes_ = format_.mBytesPerFrame * params.sample_rate * 6 / 1000; | 84 silence_bytes_ = format_.mBytesPerFrame * params.sample_rate * 6 / 1000; |
| 85 } | 85 } |
| 86 | 86 |
| 87 PCMQueueOutAudioOutputStream::~PCMQueueOutAudioOutputStream() { | 87 PCMQueueOutAudioOutputStream::~PCMQueueOutAudioOutputStream() { |
| 88 } | 88 } |
| 89 | 89 |
| 90 void PCMQueueOutAudioOutputStream::HandleError(OSStatus err) { | 90 void PCMQueueOutAudioOutputStream::HandleError(OSStatus err) { |
| 91 // source_ can be set to NULL from another thread. We need to cache its | 91 // source_ can be set to NULL from another thread. We need to cache its |
| 92 // pointer while we operate here. Note that does not mean that the source | 92 // pointer while we operate here. Note that does not mean that the source |
| 93 // has been destroyed. | 93 // has been destroyed. |
| 94 AudioSourceCallback* source = source_; | 94 AudioSourceCallback* source = GetSource(); |
| 95 if (source) | 95 if (source) |
| 96 source->OnError(this, static_cast<int>(err)); | 96 source->OnError(this, static_cast<int>(err)); |
| 97 NOTREACHED() << "error code " << err; | 97 NOTREACHED() << "error code " << err; |
| 98 } | 98 } |
| 99 | 99 |
| 100 bool PCMQueueOutAudioOutputStream::Open() { | 100 bool PCMQueueOutAudioOutputStream::Open() { |
| 101 // Get the default device id. | 101 // Get the default device id. |
| 102 AudioObjectID device_id = 0; | 102 AudioObjectID device_id = 0; |
| 103 AudioObjectPropertyAddress property_address = { | 103 AudioObjectPropertyAddress property_address = { |
| 104 kAudioHardwarePropertyDefaultOutputDevice, | 104 kAudioHardwarePropertyDefaultOutputDevice, |
| (...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 150 format_.mChannelsPerFrame = num_core_channels_; | 150 format_.mChannelsPerFrame = num_core_channels_; |
| 151 format_.mBytesPerFrame = (format_.mBitsPerChannel >> 3) * | 151 format_.mBytesPerFrame = (format_.mBitsPerChannel >> 3) * |
| 152 format_.mChannelsPerFrame; | 152 format_.mChannelsPerFrame; |
| 153 format_.mBytesPerPacket = format_.mBytesPerFrame * format_.mFramesPerPacket; | 153 format_.mBytesPerPacket = format_.mBytesPerFrame * format_.mFramesPerPacket; |
| 154 } else { | 154 } else { |
| 155 should_down_mix_ = false; | 155 should_down_mix_ = false; |
| 156 } | 156 } |
| 157 // Create the actual queue object and let the OS use its own thread to | 157 // Create the actual queue object and let the OS use its own thread to |
| 158 // run its CFRunLoop. | 158 // run its CFRunLoop. |
| 159 err = AudioQueueNewOutput(&format_, RenderCallback, this, NULL, | 159 err = AudioQueueNewOutput(&format_, RenderCallback, this, NULL, |
| 160 kCFRunLoopCommonModes, 0, &audio_queue_); | 160 kCFRunLoopCommonModes, 0, &audio_queue_); |
| 161 if (err != noErr) { | 161 if (err != noErr) { |
| 162 HandleError(err); | 162 HandleError(err); |
| 163 return false; | 163 return false; |
| 164 } | 164 } |
| 165 // Allocate the hardware-managed buffers. | 165 // Allocate the hardware-managed buffers. |
| 166 for (uint32 ix = 0; ix != kNumBuffers; ++ix) { | 166 for (uint32 ix = 0; ix != kNumBuffers; ++ix) { |
| 167 err = AudioQueueAllocateBuffer(audio_queue_, packet_size_, &buffer_[ix]); | 167 err = AudioQueueAllocateBuffer(audio_queue_, packet_size_, &buffer_[ix]); |
| 168 if (err != noErr) { | 168 if (err != noErr) { |
| 169 HandleError(err); | 169 HandleError(err); |
| 170 return false; | 170 return false; |
| (...skipping 136 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 307 HandleError(err); | 307 HandleError(err); |
| 308 } | 308 } |
| 309 // Inform the audio manager that we have been closed. This can cause our | 309 // Inform the audio manager that we have been closed. This can cause our |
| 310 // destruction. | 310 // destruction. |
| 311 manager_->ReleaseOutputStream(this); | 311 manager_->ReleaseOutputStream(this); |
| 312 } | 312 } |
| 313 | 313 |
| 314 void PCMQueueOutAudioOutputStream::Stop() { | 314 void PCMQueueOutAudioOutputStream::Stop() { |
| 315 // We request a synchronous stop, so the next call can take some time. In | 315 // We request a synchronous stop, so the next call can take some time. In |
| 316 // the windows implementation we block here as well. | 316 // the windows implementation we block here as well. |
| 317 source_ = NULL; | 317 SetSource(NULL); |
| 318 |
| 318 // We set the source to null to signal to the data queueing thread it can stop | 319 // We set the source to null to signal to the data queueing thread it can stop |
| 319 // queueing data, however at most one callback might still be in flight which | 320 // queueing data, however at most one callback might still be in flight which |
| 320 // could attempt to enqueue right after the next call. Rather that trying to | 321 // could attempt to enqueue right after the next call. Rather that trying to |
| 321 // use a lock we rely on the internal Mac queue lock so the enqueue might | 322 // use a lock we rely on the internal Mac queue lock so the enqueue might |
| 322 // succeed or might fail but it won't crash or leave the queue itself in an | 323 // succeed or might fail but it won't crash or leave the queue itself in an |
| 323 // inconsistent state. | 324 // inconsistent state. |
| 324 OSStatus err = AudioQueueStop(audio_queue_, true); | 325 OSStatus err = AudioQueueStop(audio_queue_, true); |
| 325 if (err != noErr) | 326 if (err != noErr) |
| 326 HandleError(err); | 327 HandleError(err); |
| 327 } | 328 } |
| (...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 371 core_channel_orderings_[input_channel] == kEmptyChannel && | 372 core_channel_orderings_[input_channel] == kEmptyChannel && |
| 372 kChannelOrderings[source_layout_][input_channel] > kEmptyChannel && | 373 kChannelOrderings[source_layout_][input_channel] > kEmptyChannel && |
| 373 kChannelOrderings[source_layout_][output_channel] == kEmptyChannel) { | 374 kChannelOrderings[source_layout_][output_channel] == kEmptyChannel) { |
| 374 channel_remap_[core_channel_orderings_[output_channel]] = | 375 channel_remap_[core_channel_orderings_[output_channel]] = |
| 375 kChannelOrderings[source_layout_][input_channel]; | 376 kChannelOrderings[source_layout_][input_channel]; |
| 376 return true; | 377 return true; |
| 377 } | 378 } |
| 378 return false; | 379 return false; |
| 379 } | 380 } |
| 380 | 381 |
| 381 // Note to future hackers of this function: Do not add locks here because we | 382 // Note to future hackers of this function: Do not add locks to this function |
| 382 // call out to third party source that might do crazy things including adquire | 383 // that are held through any calls made back into AudioQueue APIs, or other |
| 383 // external locks or somehow re-enter here because its legal for them to call | 384 // OS audio functions. This is because the OS dispatch may grab external |
| 384 // some audio functions. | 385 // locks, or possibly re-enter this function which can lead to a deadlock. |
| 385 void PCMQueueOutAudioOutputStream::RenderCallback(void* p_this, | 386 void PCMQueueOutAudioOutputStream::RenderCallback(void* p_this, |
| 386 AudioQueueRef queue, | 387 AudioQueueRef queue, |
| 387 AudioQueueBufferRef buffer) { | 388 AudioQueueBufferRef buffer) { |
| 388 TRACE_EVENT0("audio", "PCMQueueOutAudioOutputStream::RenderCallback"); | 389 TRACE_EVENT0("audio", "PCMQueueOutAudioOutputStream::RenderCallback"); |
| 389 | 390 |
| 390 PCMQueueOutAudioOutputStream* audio_stream = | 391 PCMQueueOutAudioOutputStream* audio_stream = |
| 391 static_cast<PCMQueueOutAudioOutputStream*>(p_this); | 392 static_cast<PCMQueueOutAudioOutputStream*>(p_this); |
| 392 // Call the audio source to fill the free buffer with data. Not having a | 393 // Call the audio source to fill the free buffer with data. Not having a |
| 393 // source means that the queue has been closed. This is not an error. | 394 // source means that the queue has been closed. This is not an error. |
| 394 AudioSourceCallback* source = audio_stream->source_; | 395 AudioSourceCallback* source = audio_stream->GetSource(); |
| 395 if (!source) | 396 if (!source) |
| 396 return; | 397 return; |
| 397 | 398 |
| 398 // Adjust the number of pending bytes by subtracting the amount played. | 399 // Adjust the number of pending bytes by subtracting the amount played. |
| 399 if (!static_cast<AudioQueueUserData*>(buffer->mUserData)->empty_buffer) | 400 if (!static_cast<AudioQueueUserData*>(buffer->mUserData)->empty_buffer) |
| 400 audio_stream->pending_bytes_ -= buffer->mAudioDataByteSize; | 401 audio_stream->pending_bytes_ -= buffer->mAudioDataByteSize; |
| 401 uint32 capacity = buffer->mAudioDataBytesCapacity; | 402 uint32 capacity = buffer->mAudioDataBytesCapacity; |
| 402 // TODO(sergeyu): Specify correct hardware delay for AudioBuffersState. | 403 // TODO(sergeyu): Specify correct hardware delay for AudioBuffersState. |
| 403 uint32 filled = source->OnMoreData( | 404 uint32 filled = source->OnMoreData( |
| 404 audio_stream, reinterpret_cast<uint8*>(buffer->mAudioData), capacity, | 405 audio_stream, reinterpret_cast<uint8*>(buffer->mAudioData), capacity, |
| (...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 460 if (!static_cast<AudioQueueUserData*>(buffer->mUserData)->empty_buffer) | 461 if (!static_cast<AudioQueueUserData*>(buffer->mUserData)->empty_buffer) |
| 461 audio_stream->pending_bytes_ += filled; | 462 audio_stream->pending_bytes_ += filled; |
| 462 if (NULL == queue) | 463 if (NULL == queue) |
| 463 return; | 464 return; |
| 464 // Queue the audio data to the audio driver. | 465 // Queue the audio data to the audio driver. |
| 465 OSStatus err = AudioQueueEnqueueBuffer(queue, buffer, 0, NULL); | 466 OSStatus err = AudioQueueEnqueueBuffer(queue, buffer, 0, NULL); |
| 466 if (err != noErr) { | 467 if (err != noErr) { |
| 467 if (err == kAudioQueueErr_EnqueueDuringReset) { | 468 if (err == kAudioQueueErr_EnqueueDuringReset) { |
| 468 // This is the error you get if you try to enqueue a buffer and the | 469 // This is the error you get if you try to enqueue a buffer and the |
| 469 // queue has been closed. Not really a problem if indeed the queue | 470 // queue has been closed. Not really a problem if indeed the queue |
| 470 // has been closed. | 471 // has been closed. We recheck the value of source now to see if it has |
| 471 if (!audio_stream->source_) | 472 // indeed been closed. |
| 473 if (!audio_stream->GetSource()) |
| 472 return; | 474 return; |
| 473 } | 475 } |
| 474 audio_stream->HandleError(err); | 476 audio_stream->HandleError(err); |
| 475 } | 477 } |
| 476 } | 478 } |
| 477 | 479 |
| 478 void PCMQueueOutAudioOutputStream::Start(AudioSourceCallback* callback) { | 480 void PCMQueueOutAudioOutputStream::Start(AudioSourceCallback* callback) { |
| 479 DCHECK(callback); | 481 DCHECK(callback); |
| 480 OSStatus err = noErr; | 482 OSStatus err = noErr; |
| 481 source_ = callback; | 483 SetSource(callback); |
| 482 pending_bytes_ = 0; | 484 pending_bytes_ = 0; |
| 483 // Ask the source to pre-fill all our buffers before playing. | 485 // Ask the source to pre-fill all our buffers before playing. |
| 484 for (uint32 ix = 0; ix != kNumBuffers; ++ix) { | 486 for (uint32 ix = 0; ix != kNumBuffers; ++ix) { |
| 485 buffer_[ix]->mAudioDataByteSize = 0; | 487 buffer_[ix]->mAudioDataByteSize = 0; |
| 486 RenderCallback(this, NULL, buffer_[ix]); | 488 RenderCallback(this, NULL, buffer_[ix]); |
| 487 } | 489 } |
| 488 | 490 |
| 489 // Queue the buffers to the audio driver, sounds starts now. | 491 // Queue the buffers to the audio driver, sounds starts now. |
| 490 for (uint32 ix = 0; ix != kNumBuffers; ++ix) { | 492 for (uint32 ix = 0; ix != kNumBuffers; ++ix) { |
| 491 err = AudioQueueEnqueueBuffer(audio_queue_, buffer_[ix], 0, NULL); | 493 err = AudioQueueEnqueueBuffer(audio_queue_, buffer_[ix], 0, NULL); |
| 492 if (err != noErr) { | 494 if (err != noErr) { |
| 493 HandleError(err); | 495 HandleError(err); |
| 494 return; | 496 return; |
| 495 } | 497 } |
| 496 } | 498 } |
| 497 err = AudioQueueStart(audio_queue_, NULL); | 499 err = AudioQueueStart(audio_queue_, NULL); |
| 498 if (err != noErr) { | 500 if (err != noErr) { |
| 499 HandleError(err); | 501 HandleError(err); |
| 500 return; | 502 return; |
| 501 } | 503 } |
| 502 } | 504 } |
| 505 |
| 506 void PCMQueueOutAudioOutputStream::SetSource(AudioSourceCallback* source) { |
| 507 base::AutoLock lock(source_lock_); |
| 508 source_ = source; |
| 509 } |
| 510 |
| 511 AudioOutputStream::AudioSourceCallback* |
| 512 PCMQueueOutAudioOutputStream::GetSource() { |
| 513 base::AutoLock lock(source_lock_); |
| 514 return source_; |
| 515 } |
| OLD | NEW |