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

Side by Side Diff: media/test/audio-hang.cc

Issue 63823006: Create minimized standalone test case for OSX audio hangs. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 7 years, 1 month 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 | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 //
2 // Minimized test case for audio playback issue where AudioUnits are started
3 // "successfully" during system resume, but never receive AURenderCallbacks.
4 //
5 // Date: Fri, Nov 15, 2013
6 // Author: Dale Curtis <dalecurtis@google.com>
7 // Original Chrome issue: http://crbug.com/160920
8 //
9 // Code based on Technical Note TN2091:
10 // https://developer.apple.com/library/mac/technotes/tn2091/_index.html
11 //
12 // Compiles with the following command line:
13 // clang -framework CoreAudio -framework AudioUnit -lstdc++ audio-hang.cc
14 //
15
16 #include <cstring>
17 #include <pthread.h>
18
19 #include <AudioUnit/AudioUnit.h>
20 #include <CoreAudio/CoreAudio.h>
21 #include <CoreServices/CoreServices.h>
22
23 // Create an AUHAL based AudioUnit.
24 static AudioUnit CreateAudioUnit() {
25 AudioComponentDescription desc = {
26 kAudioUnitType_Output,
27 kAudioUnitSubType_HALOutput,
28 kAudioUnitManufacturer_Apple,
29 0,
30 0
31 };
32
33 AudioComponent comp = AudioComponentFindNext(0, &desc);
34 assert(comp);
35
36 AudioUnit audio_unit;
37 OSStatus result = AudioComponentInstanceNew(comp, &audio_unit);
38 assert(result == noErr);
39
40 return audio_unit;
41 }
42
43 // Configure an AudioUnit to use the default output device.
44 static void ConfigureAudioUnitForDefaultDevice(AudioUnit audio_unit) {
45 const AudioObjectPropertyAddress property_address = {
46 kAudioHardwarePropertyDefaultOutputDevice,
47 kAudioObjectPropertyScopeGlobal,
48 kAudioObjectPropertyElementMaster
49 };
50
51 AudioDeviceID device_id = 0;
52 UInt32 size = sizeof(device_id);
53 OSStatus result = AudioObjectGetPropertyData(
54 kAudioObjectSystemObject, &property_address, 0, 0, &size, &device_id);
55 assert(result == kAudioHardwareNoError && device_id != kAudioDeviceUnknown);
56
57 result = AudioUnitSetProperty(audio_unit,
58 kAudioOutputUnitProperty_CurrentDevice,
59 kAudioUnitScope_Global,
60 0,
61 &device_id,
62 sizeof(device_id));
63 assert(result == noErr);
64 }
65
66 // Configure an AudioUnit for output only.
67 static void ConfigureAudioUnitForOutputOnly(AudioUnit audio_unit) {
68 // Disable input.
69 UInt32 enabled = 0;
70 OSStatus result = AudioUnitSetProperty(audio_unit,
71 kAudioOutputUnitProperty_EnableIO,
72 kAudioUnitScope_Input,
73 1,
74 &enabled,
75 sizeof(enabled));
76 assert (result == noErr);
77
78 // Enable output.
79 enabled = 1;
80 result = AudioUnitSetProperty(audio_unit,
81 kAudioOutputUnitProperty_EnableIO,
82 kAudioUnitScope_Output,
83 0,
84 &enabled,
85 sizeof(enabled));
86 assert (result == noErr);
87 }
88
89 // Configure an AudioUnit for float planar w/ native channel count and sample
90 // rate.
91 static void ConfigureAudioUnitStreamFormat(AudioUnit audio_unit) {
92 AudioStreamBasicDescription device_format = {0};
93 UInt32 size = sizeof(device_format);
94 OSStatus result = AudioUnitGetProperty(audio_unit,
95 kAudioUnitProperty_StreamFormat,
96 kAudioUnitScope_Input,
97 0,
98 &device_format,
99 &size);
100 assert(result == noErr);
101
102 AudioStreamBasicDescription desired_format = {
103 device_format.mSampleRate,
104 kAudioFormatLinearPCM,
105 kAudioFormatFlagsNativeFloatPacked | kLinearPCMFormatFlagIsNonInterleaved,
106 sizeof(Float32),
107 1,
108 sizeof(Float32),
109 device_format.mChannelsPerFrame,
110 sizeof(Float32) * 8,
111 0
112 };
113
114 result = AudioUnitSetProperty(audio_unit,
115 kAudioUnitProperty_StreamFormat,
116 kAudioUnitScope_Input,
117 0,
118 &desired_format,
119 size);
120 assert(result == noErr);
121 }
122
123 class AudioOutputStream {
124 public:
125 AudioOutputStream()
126 : audio_unit_(CreateAudioUnit()),
127 time_state_(0),
128 render_signal_(NULL) {
129 ConfigureAudioUnitForOutputOnly(audio_unit_);
130 ConfigureAudioUnitForDefaultDevice(audio_unit_);
131 ConfigureAudioUnitStreamFormat(audio_unit_);
132 ConfigureAudioUnitRenderCallback(audio_unit_, this);
133
134 OSStatus result = AudioUnitInitialize(audio_unit_);
135 assert(result == noErr);
136
137 printf("AudioOutputStream(%p) created.\n", this);
138 }
139
140 ~AudioOutputStream() {
141 assert(!render_signal_);
142
143 OSStatus result = AudioUnitUninitialize(audio_unit_);
144 assert(result == noErr);
145
146 result = AudioComponentInstanceDispose(audio_unit_);
147 assert(result == noErr);
148
149 printf("AudioOutputStream(%p) destroyed.\n", this);
150 }
151
152 void Start(pthread_cond_t* render_signal) {
153 assert(render_signal);
154 assert(!render_signal_);
155 render_signal_ = render_signal;
156
157 OSStatus result = AudioOutputUnitStart(audio_unit_);
158 assert(result == noErr);
159
160 printf(" AudioOutputStream(%p) started.\n", this);
161 }
162
163 void Stop() {
164 OSStatus result = AudioOutputUnitStop(audio_unit_);
165 assert(result == noErr);
166
167 assert(render_signal_);
168 render_signal_ = NULL;
169
170 printf(" AudioOutputStream(%p) stopped.\n", this);
171 }
172
173 void Render(UInt32 number_of_frames, AudioBufferList* io_data) {
174 assert(io_data->mNumberBuffers > 0);
175 assert(io_data->mBuffers[0].mDataByteSize ==
176 number_of_frames * sizeof(float));
177
178 // Fill the first AudioBuffer with a sine wave and copy to other channels.
179 float* output = reinterpret_cast<float*>(io_data->mBuffers[0].mData);
180 for (UInt32 i = 0; i < number_of_frames; ++i)
181 output[i] = sin(2.0 * M_PI * 600.0 / 48000.0 * time_state_++);
182 for (UInt32 i = 1; i < io_data->mNumberBuffers; ++i) {
183 memcpy(io_data->mBuffers[i].mData,
184 io_data->mBuffers[0].mData,
185 io_data->mBuffers[i].mDataByteSize);
186 }
187
188 // Signal the main loop that we've recieved the first Render() call.
189 assert(render_signal_);
190 pthread_cond_signal(render_signal_);
191 }
192
193 static OSStatus AURenderCallbackProc(void* user_data,
194 AudioUnitRenderActionFlags* flags,
195 const AudioTimeStamp* output_time_stamp,
196 UInt32 bus_number,
197 UInt32 number_of_frames,
198 AudioBufferList* io_data) {
199 assert(user_data);
200 static_cast<AudioOutputStream*>(user_data)->Render(
201 number_of_frames, io_data);
202 return noErr;
203 }
204
205 void ConfigureAudioUnitRenderCallback(AudioUnit audio_unit,
206 void* user_data) {
207 AURenderCallbackStruct callback = { AURenderCallbackProc, user_data };
208 OSStatus result = AudioUnitSetProperty(audio_unit,
209 kAudioUnitProperty_SetRenderCallback,
210 kAudioUnitScope_Input,
211 0,
212 &callback,
213 sizeof(callback));
214 assert(result == noErr);
215 }
216
217 private:
218 const AudioUnit audio_unit_;
219 size_t time_state_;
220 pthread_cond_t* render_signal_;
221 };
222
223 int main(int argc, char* argv[]) {
224 pthread_mutex_t stream_mutex = PTHREAD_MUTEX_INITIALIZER;
225 pthread_cond_t stream_1_render_called = PTHREAD_COND_INITIALIZER;
226 pthread_cond_t stream_2_render_called = PTHREAD_COND_INITIALIZER;
227
228 // Repeatedly configure and start two streams, wait for the first callback on
229 // each stream, and then close and destroy the streams. To reproduce the bug,
230 // manually suspend (close the lid) and resume (open the lid) the device until
231 // the beeping noise stops. At this point you should see the loop hung while
232 // waiting for the first callback; e.g.,
233 //
234 // AudioOutputStream(0x00000001) created.
235 // AudioOutputStream(0x00000002) created.
236 // AudioOutputStream(0x00000001) started.
237 // AudioOutputStream(0x00000002) started.
238 //
239 // Usually only a few suspend and resume cycles are necessary to reproduce the
240 // issue.
241 while (true) {
242 printf("\n");
243 AudioOutputStream stream_1;
244 AudioOutputStream stream_2;
245
246 stream_1.Start(&stream_1_render_called);
247 stream_2.Start(&stream_2_render_called);
248
249 pthread_mutex_lock(&stream_mutex);
250 pthread_cond_wait(&stream_1_render_called, &stream_mutex);
251 pthread_cond_wait(&stream_2_render_called, &stream_mutex);
252 pthread_mutex_unlock(&stream_mutex);
253
254 stream_2.Stop();
255 stream_1.Stop();
256 }
257
258 return 0;
259 }
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698