Chromium Code Reviews| 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 #include "media/audio/mac/audio_manager_mac.h" | 5 #include "media/audio/mac/audio_manager_mac.h" |
| 6 #include "media/audio/mac/audio_output_mac.h" | 6 #include "media/audio/mac/audio_output_mac.h" |
| 7 | 7 |
| 8 #include "base/basictypes.h" | 8 #include "base/basictypes.h" |
| 9 #include "base/logging.h" | 9 #include "base/logging.h" |
| 10 | 10 |
| 11 // Overview of operation: | |
|
John Grabowski
2009/04/30 23:16:24
Thanks for doing this.
| |
| 12 // 1) An object of PCMQueueOutAudioOutputStream is created by the AudioManager | |
| 13 // factory: audio_man->MakeAudioStream(). This just fills some structure. | |
| 14 // 2) Next some thread will call Open(), at that point the underliying OS | |
| 15 // queue is created and the audio buffers allocated. | |
| 16 // 3) Then some thread will call Start(source) At this point the source will be | |
| 17 // called to fill the initial buffers in the context of that same thread. | |
| 18 // Then the OS queue is started which will create its own thread which | |
| 19 // periodically will call the source for more data as buffers are being | |
| 20 // consumed. | |
| 21 // 4) At some point some thread will call Stop(), which we handle by directly | |
| 22 // stoping the OS queue. | |
| 23 // 5) One more callback to the source could be delivered in in the context of | |
| 24 // the queue's own thread. Data, if any will be discared. | |
| 25 // 6) The same thread that called stop will call Close() where we cleanup | |
| 26 // and notifiy the audio manager, which likley will destroy this object. | |
| 27 | |
| 28 // TODO(cpu): Remove the constant for this error when snow leopard arrives. | |
|
John Grabowski
2009/04/30 23:16:24
More correctly, "when we switch to the Snow Leopar
| |
| 29 enum { | |
| 30 kAudioQueueErr_EnqueueDuringReset = -66632 | |
| 31 }; | |
| 32 | |
| 11 PCMQueueOutAudioOutputStream::PCMQueueOutAudioOutputStream( | 33 PCMQueueOutAudioOutputStream::PCMQueueOutAudioOutputStream( |
| 12 AudioManagerMac* manager, int channels, int sampling_rate, | 34 AudioManagerMac* manager, int channels, int sampling_rate, |
| 13 char bits_per_sample) | 35 char bits_per_sample) |
| 14 : format_(), | 36 : format_(), |
| 15 audio_queue_(NULL), | 37 audio_queue_(NULL), |
| 16 buffer_(), | 38 buffer_(), |
| 17 source_(NULL), | 39 source_(NULL), |
| 18 manager_(manager) { | 40 manager_(manager) { |
| 19 // We must have a manager. | 41 // We must have a manager. |
| 20 DCHECK(manager_); | 42 DCHECK(manager_); |
| 21 // A frame is one sample across all channels. In interleaved audio the per | 43 // A frame is one sample across all channels. In interleaved audio the per |
| 22 // frame fields identify the set of n |channels|. In uncompressed audio, a | 44 // frame fields identify the set of n |channels|. In uncompressed audio, a |
| 23 // packet is always one frame. | 45 // packet is always one frame. |
| 24 format_.mSampleRate = sampling_rate; | 46 format_.mSampleRate = sampling_rate; |
| 25 format_.mFormatID = kAudioFormatLinearPCM; | 47 format_.mFormatID = kAudioFormatLinearPCM; |
| 26 format_.mFormatFlags = kLinearPCMFormatFlagIsPacked | | 48 format_.mFormatFlags = kLinearPCMFormatFlagIsPacked | |
| 27 kLinearPCMFormatFlagIsSignedInteger; | 49 kLinearPCMFormatFlagIsSignedInteger; |
| 28 format_.mBitsPerChannel = bits_per_sample; | 50 format_.mBitsPerChannel = bits_per_sample; |
| 29 format_.mBytesPerFrame = format_.mBytesPerPacket; | |
| 30 format_.mChannelsPerFrame = channels; | 51 format_.mChannelsPerFrame = channels; |
| 31 format_.mFramesPerPacket = 1; | 52 format_.mFramesPerPacket = 1; |
| 32 format_.mBytesPerPacket = (format_.mBitsPerChannel * channels) / 8; | 53 format_.mBytesPerPacket = (format_.mBitsPerChannel * channels) / 8; |
| 54 format_.mBytesPerFrame = format_.mBytesPerPacket; | |
| 33 } | 55 } |
| 34 | 56 |
| 35 PCMQueueOutAudioOutputStream::~PCMQueueOutAudioOutputStream() { | 57 PCMQueueOutAudioOutputStream::~PCMQueueOutAudioOutputStream() { |
| 36 } | 58 } |
| 37 | 59 |
| 38 void PCMQueueOutAudioOutputStream::HandleError(OSStatus err) { | 60 void PCMQueueOutAudioOutputStream::HandleError(OSStatus err) { |
| 39 if (source_) | 61 // source_ can be set to NULL from another thread. We need to cache its |
| 40 source_->OnError(this, static_cast<int>(err)); | 62 // pointer while we operate here. Note that does not mean that the source |
| 63 // has been destroyed. | |
| 64 AudioSourceCallback* source = source_; | |
| 65 if (source) | |
| 66 source->OnError(this, static_cast<int>(err)); | |
| 41 NOTREACHED() << "error code " << err; | 67 NOTREACHED() << "error code " << err; |
| 42 } | 68 } |
| 43 | 69 |
| 44 bool PCMQueueOutAudioOutputStream::Open(size_t packet_size) { | 70 bool PCMQueueOutAudioOutputStream::Open(size_t packet_size) { |
| 45 if (0 == packet_size) { | 71 if (0 == packet_size) { |
| 46 // TODO(cpu) : Impelement default buffer computation. | 72 // TODO(cpu) : Impelement default buffer computation. |
| 47 return false; | 73 return false; |
| 48 } | 74 } |
| 49 // Create the actual queue object and let the OS use its own thread to | 75 // Create the actual queue object and let the OS use its own thread to |
| 50 // run its CFRunLoop. | 76 // run its CFRunLoop. |
| (...skipping 21 matching lines...) Expand all Loading... | |
| 72 } | 98 } |
| 73 | 99 |
| 74 void PCMQueueOutAudioOutputStream::Close() { | 100 void PCMQueueOutAudioOutputStream::Close() { |
| 75 // It is valid to call Close() before calling Open(), thus audio_queue_ | 101 // It is valid to call Close() before calling Open(), thus audio_queue_ |
| 76 // might be NULL. | 102 // might be NULL. |
| 77 if (audio_queue_) { | 103 if (audio_queue_) { |
| 78 OSStatus err = 0; | 104 OSStatus err = 0; |
| 79 for (size_t ix = 0; ix != kNumBuffers; ++ix) { | 105 for (size_t ix = 0; ix != kNumBuffers; ++ix) { |
| 80 if (buffer_[ix]) { | 106 if (buffer_[ix]) { |
| 81 err = AudioQueueFreeBuffer(audio_queue_, buffer_[ix]); | 107 err = AudioQueueFreeBuffer(audio_queue_, buffer_[ix]); |
| 82 if (err) { | 108 if (err != noErr) { |
| 83 HandleError(err); | 109 HandleError(err); |
| 84 break; | 110 break; |
| 85 } | 111 } |
| 86 } | 112 } |
| 87 } | 113 } |
| 88 err = AudioQueueDispose(audio_queue_, true); | 114 err = AudioQueueDispose(audio_queue_, true); |
| 89 if (err) { | 115 if (err != noErr) |
| 90 HandleError(err); | 116 HandleError(err); |
| 91 } | |
| 92 } | 117 } |
| 93 // Inform the audio manager that we have been closed. This can cause our | 118 // Inform the audio manager that we have been closed. This can cause our |
| 94 // destruction. | 119 // destruction. |
| 95 manager_->ReleaseStream(this); | 120 manager_->ReleaseStream(this); |
| 96 } | 121 } |
| 97 | 122 |
| 98 void PCMQueueOutAudioOutputStream::Stop() { | 123 void PCMQueueOutAudioOutputStream::Stop() { |
| 99 // TODO(cpu): Implement. | 124 // We request a synchronous stop, so the next call can take some time. In |
| 125 // the windows implementation we block here as well. | |
| 126 source_ = NULL; | |
| 127 // We set the source to null to signal to the data queueing thread it can stop | |
| 128 // queueing data, however at most one callback might still be in flight which | |
| 129 // could attempt to enqueue right after the next call. Rather that trying to | |
| 130 // use a lock we rely on the internal Mac queue lock so the enqueue might | |
| 131 // succeed or might fail but it won't crash or leave the queue itself in an | |
| 132 // inconsistent state. | |
| 133 OSStatus err = AudioQueueStop(audio_queue_, true); | |
| 134 if (err != noErr) | |
| 135 HandleError(err); | |
| 100 } | 136 } |
| 101 | 137 |
| 102 void PCMQueueOutAudioOutputStream::SetVolume(double left_level, | 138 void PCMQueueOutAudioOutputStream::SetVolume(double left_level, |
| 103 double right_level) { | 139 double right_level) { |
| 104 // TODO(cpu): Implement. | 140 // TODO(cpu): Implement. |
| 105 } | 141 } |
| 106 | 142 |
| 107 void PCMQueueOutAudioOutputStream::GetVolume(double* left_level, | 143 void PCMQueueOutAudioOutputStream::GetVolume(double* left_level, |
| 108 double* right_level) { | 144 double* right_level) { |
| 109 // TODO(cpu): Implement. | 145 // TODO(cpu): Implement. |
| 110 } | 146 } |
| 111 | 147 |
| 112 size_t PCMQueueOutAudioOutputStream::GetNumBuffers() { | 148 size_t PCMQueueOutAudioOutputStream::GetNumBuffers() { |
| 113 return kNumBuffers; | 149 return kNumBuffers; |
| 114 } | 150 } |
| 115 | 151 |
| 152 // Note to future hackers of this function: Do not add locks here because we | |
| 153 // call out to third party source that might do crazy things including adquire | |
| 154 // external locks or somehow re-enter here because its legal for them to call | |
| 155 // some audio functions. | |
| 116 void PCMQueueOutAudioOutputStream::RenderCallback(void* p_this, | 156 void PCMQueueOutAudioOutputStream::RenderCallback(void* p_this, |
| 117 AudioQueueRef queue, | 157 AudioQueueRef queue, |
| 118 AudioQueueBufferRef buffer) { | 158 AudioQueueBufferRef buffer) { |
| 119 PCMQueueOutAudioOutputStream* audio_stream = | 159 PCMQueueOutAudioOutputStream* audio_stream = |
| 120 static_cast<PCMQueueOutAudioOutputStream*>(p_this); | 160 static_cast<PCMQueueOutAudioOutputStream*>(p_this); |
| 121 // Call the audio source to fill the free buffer with data. | 161 // Call the audio source to fill the free buffer with data. Not having a |
| 162 // source means that the queue has been closed. This is not an error. | |
| 163 AudioSourceCallback* source = audio_stream->source_; | |
| 164 if (!source) | |
| 165 return; | |
| 122 size_t capacity = buffer->mAudioDataBytesCapacity; | 166 size_t capacity = buffer->mAudioDataBytesCapacity; |
| 123 size_t filled = audio_stream->source_->OnMoreData(audio_stream, | 167 size_t filled = source->OnMoreData(audio_stream, buffer->mAudioData, capacity) ; |
| 124 buffer->mAudioData, | |
| 125 capacity); | |
| 126 if (filled > capacity) { | 168 if (filled > capacity) { |
| 127 // User probably overran our buffer. | 169 // User probably overran our buffer. |
| 128 audio_stream->HandleError(0); | 170 audio_stream->HandleError(0); |
| 129 return; | 171 return; |
| 130 } | 172 } |
| 173 buffer->mAudioDataByteSize = filled; | |
| 174 if (NULL == queue) | |
| 175 return; | |
| 131 // Queue the audio data to the audio driver. | 176 // Queue the audio data to the audio driver. |
| 132 buffer->mAudioDataByteSize = filled; | |
| 133 OSStatus err = AudioQueueEnqueueBuffer(queue, buffer, 0, NULL); | 177 OSStatus err = AudioQueueEnqueueBuffer(queue, buffer, 0, NULL); |
| 134 if (err != noErr) | 178 if (err != noErr) { |
| 179 if (err == kAudioQueueErr_EnqueueDuringReset) { | |
| 180 // This is the error you get if you try to enqueue a buffer and the | |
| 181 // queue has been closed. Not really a problem if indeed the queue | |
| 182 // has been closed. | |
| 183 if (!audio_stream->source_) | |
| 184 return; | |
| 185 } | |
| 135 audio_stream->HandleError(err); | 186 audio_stream->HandleError(err); |
| 187 } | |
| 136 } | 188 } |
| 137 | 189 |
| 138 void PCMQueueOutAudioOutputStream::Start(AudioSourceCallback* callback) { | 190 void PCMQueueOutAudioOutputStream::Start(AudioSourceCallback* callback) { |
| 191 DCHECK(callback); | |
| 139 OSStatus err = AudioQueueStart(audio_queue_, NULL); | 192 OSStatus err = AudioQueueStart(audio_queue_, NULL); |
| 140 if (err != noErr) { | 193 if (err != noErr) { |
| 141 HandleError(err); | 194 HandleError(err); |
| 142 return; | 195 return; |
| 143 } | 196 } |
| 144 // TODO(cpu) : Prefill the avaiable buffers. | 197 source_ = callback; |
| 198 // Ask the source to pre-fill all our buffers before playing. | |
| 199 for(size_t ix = 0; ix != kNumBuffers; ++ix) { | |
| 200 RenderCallback(this, NULL, buffer_[ix]); | |
| 201 } | |
| 202 // Queue the buffers to the audio driver, sounds starts now. | |
| 203 for(size_t ix = 0; ix != kNumBuffers; ++ix) { | |
| 204 err = AudioQueueEnqueueBuffer(audio_queue_, buffer_[ix], 0, NULL); | |
| 205 if (err != noErr) { | |
| 206 HandleError(err); | |
| 207 return; | |
| 208 } | |
| 209 } | |
| 145 } | 210 } |
| 211 | |
| OLD | NEW |