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 |