Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(191)

Side by Side Diff: media/audio/mac/audio_output_mac.cc

Issue 92131: Third installement of low level audio for mac... (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: '' Created 11 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « no previous file | media/audio/mac/audio_output_mac_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
OLDNEW
« no previous file with comments | « no previous file | media/audio/mac/audio_output_mac_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698