| 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/win/waveout_output_win.h" | 5 #include "media/audio/win/waveout_output_win.h" |
| 6 | 6 |
| 7 #include <windows.h> | 7 #include <windows.h> |
| 8 #include <mmsystem.h> | 8 #include <mmsystem.h> |
| 9 #pragma comment(lib, "winmm.lib") | 9 #pragma comment(lib, "winmm.lib") |
| 10 | 10 |
| 11 #include "base/basictypes.h" | 11 #include "base/basictypes.h" |
| 12 #include "base/debug/trace_event.h" | 12 #include "base/debug/trace_event.h" |
| 13 #include "base/logging.h" | 13 #include "base/logging.h" |
| 14 #include "media/audio/audio_io.h" | 14 #include "media/audio/audio_io.h" |
| 15 #include "media/audio/audio_util.h" | 15 #include "media/audio/audio_util.h" |
| 16 #include "media/audio/win/audio_manager_win.h" | 16 #include "media/audio/win/audio_manager_win.h" |
| 17 | 17 |
| 18 // Number of times InitializeCriticalSectionAndSpinCount() spins | |
| 19 // before going to sleep. | |
| 20 const DWORD kSpinCount = 2000; | |
| 21 | |
| 22 // Some general thoughts about the waveOut API which is badly documented : | 18 // Some general thoughts about the waveOut API which is badly documented : |
| 23 // - We use CALLBACK_FUNCTION mode in which XP secretly creates two threads | 19 // - We use CALLBACK_EVENT mode in which XP signals events such as buffer |
| 24 // named _MixerCallbackThread and _waveThread which have real-time priority. | 20 // releases. |
| 25 // The callbacks occur in _waveThread. | 21 // - We use RegisterWaitForSingleObject() so one of threads in thread pool |
| 22 // automatically calls our callback that feeds more data to Windows. |
| 26 // - Windows does not provide a way to query if the device is playing or paused | 23 // - Windows does not provide a way to query if the device is playing or paused |
| 27 // thus it forces you to maintain state, which naturally is not exactly | 24 // thus it forces you to maintain state, which naturally is not exactly |
| 28 // synchronized to the actual device state. | 25 // synchronized to the actual device state. |
| 29 // - Some functions, like waveOutReset cannot be called in the callback thread | |
| 30 // or called in any random state because they deadlock. This results in a | |
| 31 // non- instantaneous Stop() method. waveOutPrepareHeader seems to be in the | |
| 32 // same boat. | |
| 33 // - waveOutReset() will forcefully kill the _waveThread so it is important | |
| 34 // to make sure we are not executing inside the audio source's OnMoreData() | |
| 35 // or that we take locks inside WaveCallback() or QueueNextPacket(). | |
| 36 | 26 |
| 37 // Sixty four MB is the maximum buffer size per AudioOutputStream. | 27 // Sixty four MB is the maximum buffer size per AudioOutputStream. |
| 38 static const uint32 kMaxOpenBufferSize = 1024 * 1024 * 64; | 28 static const uint32 kMaxOpenBufferSize = 1024 * 1024 * 64; |
| 39 | 29 |
| 40 // Our sound buffers are allocated once and kept in a linked list using the | |
| 41 // the WAVEHDR::dwUser variable. The last buffer points to the first buffer. | |
| 42 static WAVEHDR* GetNextBuffer(WAVEHDR* current) { | |
| 43 return reinterpret_cast<WAVEHDR*>(current->dwUser); | |
| 44 } | |
| 45 | |
| 46 // See Also | 30 // See Also |
| 47 // http://www.thx.com/consumer/home-entertainment/home-theater/surround-sound-sp
eaker-set-up/ | 31 // http://www.thx.com/consumer/home-entertainment/home-theater/surround-sound-sp
eaker-set-up/ |
| 48 // http://en.wikipedia.org/wiki/Surround_sound | 32 // http://en.wikipedia.org/wiki/Surround_sound |
| 49 | 33 |
| 50 static const int kMaxChannelsToMask = 8; | 34 static const int kMaxChannelsToMask = 8; |
| 51 static const unsigned int kChannelsToMask[kMaxChannelsToMask + 1] = { | 35 static const unsigned int kChannelsToMask[kMaxChannelsToMask + 1] = { |
| 52 0, | 36 0, |
| 53 // 1 = Mono | 37 // 1 = Mono |
| 54 SPEAKER_FRONT_CENTER, | 38 SPEAKER_FRONT_CENTER, |
| 55 // 2 = Stereo | 39 // 2 = Stereo |
| (...skipping 16 matching lines...) Expand all Loading... |
| 72 SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT | | 56 SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT | |
| 73 SPEAKER_BACK_CENTER, | 57 SPEAKER_BACK_CENTER, |
| 74 // 8 = 7.1 | 58 // 8 = 7.1 |
| 75 SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | | 59 SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | |
| 76 SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | | 60 SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | |
| 77 SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT | | 61 SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT | |
| 78 SPEAKER_SIDE_LEFT | SPEAKER_SIDE_RIGHT | 62 SPEAKER_SIDE_LEFT | SPEAKER_SIDE_RIGHT |
| 79 // TODO(fbarchard): Add additional masks for 7.2 and beyond. | 63 // TODO(fbarchard): Add additional masks for 7.2 and beyond. |
| 80 }; | 64 }; |
| 81 | 65 |
| 66 inline size_t PCMWaveOutAudioOutputStream::BufferSize() const { |
| 67 // Round size of buffer up to the nearest 16 bytes. |
| 68 return (sizeof(WAVEHDR) + buffer_size_ + 15u) & static_cast<size_t>(~15); |
| 69 } |
| 70 |
| 71 inline WAVEHDR* PCMWaveOutAudioOutputStream::GetBuffer(int n) const { |
| 72 DCHECK_GE(n, 0); |
| 73 DCHECK_LT(n, num_buffers_); |
| 74 return reinterpret_cast<WAVEHDR*>(&buffers_[n * BufferSize()]); |
| 75 } |
| 76 |
| 77 |
| 82 PCMWaveOutAudioOutputStream::PCMWaveOutAudioOutputStream( | 78 PCMWaveOutAudioOutputStream::PCMWaveOutAudioOutputStream( |
| 83 AudioManagerWin* manager, const AudioParameters& params, int num_buffers, | 79 AudioManagerWin* manager, const AudioParameters& params, int num_buffers, |
| 84 UINT device_id) | 80 UINT device_id) |
| 85 : state_(PCMA_BRAND_NEW), | 81 : state_(PCMA_BRAND_NEW), |
| 86 manager_(manager), | 82 manager_(manager), |
| 87 device_id_(device_id), | 83 device_id_(device_id), |
| 88 waveout_(NULL), | 84 waveout_(NULL), |
| 89 callback_(NULL), | 85 callback_(NULL), |
| 90 num_buffers_(num_buffers), | 86 num_buffers_(num_buffers), |
| 91 buffer_(NULL), | |
| 92 buffer_size_(params.GetPacketSize()), | 87 buffer_size_(params.GetPacketSize()), |
| 93 volume_(1), | 88 volume_(1), |
| 94 channels_(params.channels), | 89 channels_(params.channels), |
| 95 pending_bytes_(0) { | 90 pending_bytes_(0) { |
| 96 ::InitializeCriticalSectionAndSpinCount(&lock_, kSpinCount); | |
| 97 | |
| 98 format_.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE; | 91 format_.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE; |
| 99 format_.Format.nChannels = params.channels; | 92 format_.Format.nChannels = params.channels; |
| 100 format_.Format.nSamplesPerSec = params.sample_rate; | 93 format_.Format.nSamplesPerSec = params.sample_rate; |
| 101 format_.Format.wBitsPerSample = params.bits_per_sample; | 94 format_.Format.wBitsPerSample = params.bits_per_sample; |
| 102 format_.Format.cbSize = sizeof(format_) - sizeof(WAVEFORMATEX); | 95 format_.Format.cbSize = sizeof(format_) - sizeof(WAVEFORMATEX); |
| 103 // The next are computed from above. | 96 // The next are computed from above. |
| 104 format_.Format.nBlockAlign = (format_.Format.nChannels * | 97 format_.Format.nBlockAlign = (format_.Format.nChannels * |
| 105 format_.Format.wBitsPerSample) / 8; | 98 format_.Format.wBitsPerSample) / 8; |
| 106 format_.Format.nAvgBytesPerSec = format_.Format.nBlockAlign * | 99 format_.Format.nAvgBytesPerSec = format_.Format.nBlockAlign * |
| 107 format_.Format.nSamplesPerSec; | 100 format_.Format.nSamplesPerSec; |
| 108 if (params.channels > kMaxChannelsToMask) { | 101 if (params.channels > kMaxChannelsToMask) { |
| 109 format_.dwChannelMask = kChannelsToMask[kMaxChannelsToMask]; | 102 format_.dwChannelMask = kChannelsToMask[kMaxChannelsToMask]; |
| 110 } else { | 103 } else { |
| 111 format_.dwChannelMask = kChannelsToMask[params.channels]; | 104 format_.dwChannelMask = kChannelsToMask[params.channels]; |
| 112 } | 105 } |
| 113 format_.SubFormat = KSDATAFORMAT_SUBTYPE_PCM; | 106 format_.SubFormat = KSDATAFORMAT_SUBTYPE_PCM; |
| 114 format_.Samples.wValidBitsPerSample = params.bits_per_sample; | 107 format_.Samples.wValidBitsPerSample = params.bits_per_sample; |
| 115 } | 108 } |
| 116 | 109 |
| 117 PCMWaveOutAudioOutputStream::~PCMWaveOutAudioOutputStream() { | 110 PCMWaveOutAudioOutputStream::~PCMWaveOutAudioOutputStream() { |
| 118 DCHECK(NULL == waveout_); | 111 DCHECK(NULL == waveout_); |
| 119 ::DeleteCriticalSection(&lock_); | |
| 120 } | 112 } |
| 121 | 113 |
| 122 bool PCMWaveOutAudioOutputStream::Open() { | 114 bool PCMWaveOutAudioOutputStream::Open() { |
| 123 if (state_ != PCMA_BRAND_NEW) | 115 if (state_ != PCMA_BRAND_NEW) |
| 124 return false; | 116 return false; |
| 117 if (BufferSize() * num_buffers_ > kMaxOpenBufferSize) |
| 118 return false; |
| 125 if (num_buffers_ < 2 || num_buffers_ > 5) | 119 if (num_buffers_ < 2 || num_buffers_ > 5) |
| 126 return false; | 120 return false; |
| 127 // Open the device. We'll be getting callback in WaveCallback function. | 121 |
| 128 // They occur in a magic, time-critical thread that windows creates. | 122 // Create buffer event. |
| 129 MMRESULT result = ::waveOutOpen(&waveout_, device_id_, | 123 buffer_event_.Set(::CreateEvent(NULL, // Security attributes. |
| 130 reinterpret_cast<LPCWAVEFORMATEX>(&format_), | 124 FALSE, // It will auto-reset. |
| 131 reinterpret_cast<DWORD_PTR>(WaveCallback), | 125 FALSE, // Initial state. |
| 132 reinterpret_cast<DWORD_PTR>(this), | 126 NULL)); // No name. |
| 133 CALLBACK_FUNCTION); | 127 if (!buffer_event_.Get()) |
| 128 return false; |
| 129 |
| 130 // Open the device. |
| 131 // We'll be getting buffer_event_ events when it's time to refill the buffer. |
| 132 MMRESULT result = ::waveOutOpen( |
| 133 &waveout_, |
| 134 device_id_, |
| 135 reinterpret_cast<LPCWAVEFORMATEX>(&format_), |
| 136 reinterpret_cast<DWORD_PTR>(buffer_event_.Get()), |
| 137 NULL, |
| 138 CALLBACK_EVENT); |
| 134 if (result != MMSYSERR_NOERROR) | 139 if (result != MMSYSERR_NOERROR) |
| 135 return false; | 140 return false; |
| 136 | 141 |
| 137 SetupBuffers(); | 142 SetupBuffers(); |
| 138 state_ = PCMA_READY; | 143 state_ = PCMA_READY; |
| 139 return true; | 144 return true; |
| 140 } | 145 } |
| 141 | 146 |
| 142 void PCMWaveOutAudioOutputStream::SetupBuffers() { | 147 void PCMWaveOutAudioOutputStream::SetupBuffers() { |
| 143 WAVEHDR* last = NULL; | 148 buffers_.reset(new char[BufferSize() * num_buffers_]); |
| 144 WAVEHDR* first = NULL; | |
| 145 for (int ix = 0; ix != num_buffers_; ++ix) { | 149 for (int ix = 0; ix != num_buffers_; ++ix) { |
| 146 uint32 sz = sizeof(WAVEHDR) + buffer_size_; | 150 WAVEHDR* buffer = GetBuffer(ix); |
| 147 buffer_ = reinterpret_cast<WAVEHDR*>(new char[sz]); | 151 buffer->lpData = reinterpret_cast<char*>(buffer) + sizeof(WAVEHDR); |
| 148 buffer_->lpData = reinterpret_cast<char*>(buffer_) + sizeof(WAVEHDR); | 152 buffer->dwBufferLength = buffer_size_; |
| 149 buffer_->dwBufferLength = buffer_size_; | 153 buffer->dwBytesRecorded = 0; |
| 150 buffer_->dwBytesRecorded = 0; | 154 buffer->dwFlags = WHDR_DONE; |
| 151 buffer_->dwUser = reinterpret_cast<DWORD_PTR>(last); | 155 buffer->dwLoops = 0; |
| 152 buffer_->dwFlags = WHDR_DONE; | |
| 153 buffer_->dwLoops = 0; | |
| 154 if (ix == 0) | |
| 155 first = buffer_; | |
| 156 last = buffer_; | |
| 157 // Tell windows sound drivers about our buffers. Not documented what | 156 // Tell windows sound drivers about our buffers. Not documented what |
| 158 // this does but we can guess that causes the OS to keep a reference to | 157 // this does but we can guess that causes the OS to keep a reference to |
| 159 // the memory pages so the driver can use them without worries. | 158 // the memory pages so the driver can use them without worries. |
| 160 ::waveOutPrepareHeader(waveout_, buffer_, sizeof(WAVEHDR)); | 159 ::waveOutPrepareHeader(waveout_, buffer, sizeof(WAVEHDR)); |
| 161 } | 160 } |
| 162 // Fix the first buffer to point to the last one. | |
| 163 first->dwUser = reinterpret_cast<DWORD_PTR>(last); | |
| 164 } | 161 } |
| 165 | 162 |
| 166 void PCMWaveOutAudioOutputStream::FreeBuffers() { | 163 void PCMWaveOutAudioOutputStream::FreeBuffers() { |
| 167 WAVEHDR* current = buffer_; | |
| 168 for (int ix = 0; ix != num_buffers_; ++ix) { | 164 for (int ix = 0; ix != num_buffers_; ++ix) { |
| 169 WAVEHDR* next = GetNextBuffer(current); | 165 ::waveOutUnprepareHeader(waveout_, GetBuffer(ix), sizeof(WAVEHDR)); |
| 170 ::waveOutUnprepareHeader(waveout_, current, sizeof(WAVEHDR)); | |
| 171 delete[] reinterpret_cast<char*>(current); | |
| 172 current = next; | |
| 173 } | 166 } |
| 174 buffer_ = NULL; | 167 buffers_.reset(NULL); |
| 175 } | 168 } |
| 176 | 169 |
| 177 // Initially we ask the source to fill up both audio buffers. If we don't do | 170 // Initially we ask the source to fill up all audio buffers. If we don't do |
| 178 // this then we would always get the driver callback when it is about to run | 171 // this then we would always get the driver callback when it is about to run |
| 179 // samples and that would leave too little time to react. | 172 // samples and that would leave too little time to react. |
| 180 void PCMWaveOutAudioOutputStream::Start(AudioSourceCallback* callback) { | 173 void PCMWaveOutAudioOutputStream::Start(AudioSourceCallback* callback) { |
| 181 if (state_ != PCMA_READY) | 174 if (state_ != PCMA_READY) |
| 182 return; | 175 return; |
| 183 callback_ = callback; | 176 callback_ = callback; |
| 177 |
| 178 // Start watching for buffer events. |
| 179 { |
| 180 HANDLE waiting_handle = NULL; |
| 181 ::RegisterWaitForSingleObject(&waiting_handle, |
| 182 buffer_event_.Get(), |
| 183 &BufferCallback, |
| 184 this, |
| 185 INFINITE, |
| 186 WT_EXECUTEDEFAULT); |
| 187 if (!waiting_handle) { |
| 188 HandleError(MMSYSERR_ERROR); |
| 189 return; |
| 190 } |
| 191 waiting_handle_.Set(waiting_handle); |
| 192 } |
| 193 |
| 184 state_ = PCMA_PLAYING; | 194 state_ = PCMA_PLAYING; |
| 195 |
| 196 // Queue the buffers. |
| 185 pending_bytes_ = 0; | 197 pending_bytes_ = 0; |
| 186 WAVEHDR* buffer = buffer_; | |
| 187 for (int ix = 0; ix != num_buffers_; ++ix) { | 198 for (int ix = 0; ix != num_buffers_; ++ix) { |
| 199 WAVEHDR* buffer = GetBuffer(ix); |
| 188 // Caller waits for 1st packet to become available, but not for others, | 200 // Caller waits for 1st packet to become available, but not for others, |
| 189 // so we wait for them here. | 201 // so we wait for them here. |
| 190 if (ix != 0) | 202 if (ix != 0) |
| 191 callback_->WaitTillDataReady(); | 203 callback_->WaitTillDataReady(); |
| 192 QueueNextPacket(buffer); // Read more data. | 204 QueueNextPacket(buffer); // Read more data. |
| 193 pending_bytes_ += buffer->dwBufferLength; | 205 pending_bytes_ += buffer->dwBufferLength; |
| 194 buffer = GetNextBuffer(buffer); | |
| 195 } | 206 } |
| 196 buffer = buffer_; | |
| 197 | 207 |
| 198 // From now on |pending_bytes_| would be accessed by callback thread. | 208 // From now on |pending_bytes_| would be accessed by callback thread. |
| 199 // Most likely waveOutPause() or waveOutRestart() has its own memory barrier, | 209 // Most likely waveOutPause() or waveOutRestart() has its own memory barrier, |
| 200 // but issuing our own is safer. | 210 // but issuing our own is safer. |
| 201 MemoryBarrier(); | 211 MemoryBarrier(); |
| 202 | 212 |
| 203 MMRESULT result = ::waveOutPause(waveout_); | 213 MMRESULT result = ::waveOutPause(waveout_); |
| 204 if (result != MMSYSERR_NOERROR) { | 214 if (result != MMSYSERR_NOERROR) { |
| 205 HandleError(result); | 215 HandleError(result); |
| 206 return; | 216 return; |
| 207 } | 217 } |
| 208 | 218 |
| 209 // Send the buffers to the audio driver. Note that the device is paused | 219 // Send the buffers to the audio driver. Note that the device is paused |
| 210 // so we avoid entering the callback method while still here. | 220 // so we avoid entering the callback method while still here. |
| 211 for (int ix = 0; ix != num_buffers_; ++ix) { | 221 for (int ix = 0; ix != num_buffers_; ++ix) { |
| 212 result = ::waveOutWrite(waveout_, buffer, sizeof(WAVEHDR)); | 222 result = ::waveOutWrite(waveout_, GetBuffer(ix), sizeof(WAVEHDR)); |
| 213 if (result != MMSYSERR_NOERROR) { | 223 if (result != MMSYSERR_NOERROR) { |
| 214 HandleError(result); | 224 HandleError(result); |
| 215 break; | 225 break; |
| 216 } | 226 } |
| 217 buffer = GetNextBuffer(buffer); | |
| 218 } | 227 } |
| 219 result = ::waveOutRestart(waveout_); | 228 result = ::waveOutRestart(waveout_); |
| 220 if (result != MMSYSERR_NOERROR) { | 229 if (result != MMSYSERR_NOERROR) { |
| 221 HandleError(result); | 230 HandleError(result); |
| 222 return; | 231 return; |
| 223 } | 232 } |
| 224 } | 233 } |
| 225 | 234 |
| 226 // Stopping is tricky. First, no buffer should be locked by the audio driver | 235 // Stopping is tricky if we want it be fast. |
| 227 // or else the waveOutReset() will deadlock and secondly, the callback should | 236 // For now just do it synchronously and avoid all the complexities. |
| 228 // not be inside the AudioSource's OnMoreData because waveOutReset() forcefully | 237 // TODO(enal): if we want faster Stop() we can create singleton that keeps track |
| 229 // kills the callback thread after releasing all buffers. | 238 // of all currently playing streams. Then you don't have to wait |
| 239 // till all callbacks are completed. Of course access to singleton |
| 240 // should be under its own lock, and checking the liveness and |
| 241 // acquiring the lock on stream should be done atomically. |
| 230 void PCMWaveOutAudioOutputStream::Stop() { | 242 void PCMWaveOutAudioOutputStream::Stop() { |
| 231 if (state_ != PCMA_PLAYING) | 243 if (state_ != PCMA_PLAYING) |
| 232 return; | 244 return; |
| 245 state_ = PCMA_STOPPING; |
| 246 MemoryBarrier(); |
| 233 | 247 |
| 234 // Enter into critical section and call ::waveOutReset(). The fact that we | 248 // Stop playback. |
| 235 // entered critical section means that callback is out of critical section and | |
| 236 // it is safe to reset. | |
| 237 ::EnterCriticalSection(&lock_); | |
| 238 MMRESULT res = ::waveOutReset(waveout_); | 249 MMRESULT res = ::waveOutReset(waveout_); |
| 239 ::LeaveCriticalSection(&lock_); | |
| 240 if (res != MMSYSERR_NOERROR) { | 250 if (res != MMSYSERR_NOERROR) { |
| 251 state_ = PCMA_PLAYING; |
| 241 HandleError(res); | 252 HandleError(res); |
| 242 return; | 253 return; |
| 243 } | 254 } |
| 244 | 255 |
| 256 // Stop watching for buffer event, wait till all the callbacks are complete. |
| 257 BOOL unregister = UnregisterWaitEx(waiting_handle_.Take(), |
| 258 INVALID_HANDLE_VALUE); |
| 259 if (!unregister) { |
| 260 state_ = PCMA_PLAYING; |
| 261 HandleError(MMSYSERR_ERROR); |
| 262 return; |
| 263 } |
| 264 |
| 265 // waveOutReset() leaves buffers in the unpredictable state, causing |
| 266 // problems if we want to release or reuse them. Fix the states. |
| 267 for (int ix = 0; ix != num_buffers_; ++ix) { |
| 268 GetBuffer(ix)->dwFlags = WHDR_PREPARED; |
| 269 } |
| 270 |
| 245 // Don't use callback after Stop(). | 271 // Don't use callback after Stop(). |
| 246 callback_ = NULL; | 272 callback_ = NULL; |
| 247 | 273 |
| 248 state_ = PCMA_READY; | 274 state_ = PCMA_READY; |
| 249 } | 275 } |
| 250 | 276 |
| 251 // We can Close in any state except that trying to close a stream that is | 277 // We can Close in any state except that trying to close a stream that is |
| 252 // playing Windows generates an error, which we propagate to the source. | 278 // playing Windows generates an error, which we propagate to the source. |
| 253 void PCMWaveOutAudioOutputStream::Close() { | 279 void PCMWaveOutAudioOutputStream::Close() { |
| 280 Stop(); // Just to be sure. No-op if not playing. |
| 254 if (waveout_) { | 281 if (waveout_) { |
| 255 // waveOutClose generates a callback with WOM_CLOSE id in the same thread. | |
| 256 MMRESULT res = ::waveOutClose(waveout_); | 282 MMRESULT res = ::waveOutClose(waveout_); |
| 257 if (res != MMSYSERR_NOERROR) { | 283 if (res != MMSYSERR_NOERROR) { |
| 258 HandleError(res); | 284 HandleError(res); |
| 259 return; | 285 return; |
| 260 } | 286 } |
| 261 state_ = PCMA_CLOSED; | 287 state_ = PCMA_CLOSED; |
| 262 waveout_ = NULL; | 288 waveout_ = NULL; |
| 263 FreeBuffers(); | 289 FreeBuffers(); |
| 264 } | 290 } |
| 265 // Tell the audio manager that we have been released. This can result in | 291 // Tell the audio manager that we have been released. This can result in |
| (...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 309 format_.Format.wBitsPerSample >> 3, | 335 format_.Format.wBitsPerSample >> 3, |
| 310 volume_); | 336 volume_); |
| 311 } | 337 } |
| 312 } else { | 338 } else { |
| 313 HandleError(0); | 339 HandleError(0); |
| 314 return; | 340 return; |
| 315 } | 341 } |
| 316 buffer->dwFlags = WHDR_PREPARED; | 342 buffer->dwFlags = WHDR_PREPARED; |
| 317 } | 343 } |
| 318 | 344 |
| 319 // Windows call us back in this function when some events happen. Most notably | 345 // One of the threads in our thread pool asynchronously calls this function when |
| 320 // when it is done playing a buffer. Since we use double buffering it is | 346 // buffer_event_ is signalled. Search through all the buffers looking for freed |
| 321 // convenient to think of |buffer| as free and GetNextBuffer(buffer) as in | 347 // ones, fills them with data, and "feed" the Windows. |
| 322 // use by the driver. | 348 // Note: by searching through all the buffers we guarantee that we fill all the |
| 323 void PCMWaveOutAudioOutputStream::WaveCallback(HWAVEOUT hwo, UINT msg, | 349 // buffers, even when "event loss" happens, i.e. if Windows signals event |
| 324 DWORD_PTR instance, | 350 // when it did not flip into unsignaled state from the previous signal. |
| 325 DWORD_PTR param1, DWORD_PTR) { | 351 void NTAPI PCMWaveOutAudioOutputStream::BufferCallback(PVOID lpParameter, |
| 326 TRACE_EVENT0("audio", "PCMWaveOutAudioOutputStream::WaveCallback"); | 352 BOOLEAN timer_fired) { |
| 353 TRACE_EVENT0("audio", "PCMWaveOutAudioOutputStream::BufferCallback"); |
| 327 | 354 |
| 328 if (msg == WOM_DONE) { | 355 DCHECK(!timer_fired); |
| 329 // WOM_DONE indicates that the driver is done with our buffer, we can | 356 PCMWaveOutAudioOutputStream* stream = |
| 330 // either ask the source for more data or check if we need to stop playing. | 357 reinterpret_cast<PCMWaveOutAudioOutputStream*>(lpParameter); |
| 331 WAVEHDR* buffer = reinterpret_cast<WAVEHDR*>(param1); | |
| 332 buffer->dwFlags = WHDR_DONE; | |
| 333 | 358 |
| 334 PCMWaveOutAudioOutputStream* stream = | 359 // Lock the stream so callbacks do not interfere with each other. |
| 335 reinterpret_cast<PCMWaveOutAudioOutputStream*>(instance); | 360 // Several callbacks can be called simultaneously by different threads in the |
| 361 // thread pool if some of the callbacks are slow, or system is very busy and |
| 362 // scheduled callbacks are not called on time. |
| 363 base::AutoLock auto_lock(stream->lock_); |
| 364 if (stream->state_ != PCMA_PLAYING) |
| 365 return; |
| 336 | 366 |
| 337 // Do real work only if main thread has not yet called waveOutReset(). | 367 for (int ix = 0; ix != stream->num_buffers_; ++ix) { |
| 338 if (::TryEnterCriticalSection(&stream->lock_)) { | 368 WAVEHDR* buffer = stream->GetBuffer(ix); |
| 369 if (buffer->dwFlags & WHDR_DONE) { |
| 339 // Before we queue the next packet, we need to adjust the number of | 370 // Before we queue the next packet, we need to adjust the number of |
| 340 // pending bytes since the last write to hardware. | 371 // pending bytes since the last write to hardware. |
| 341 stream->pending_bytes_ -= buffer->dwBufferLength; | 372 stream->pending_bytes_ -= buffer->dwBufferLength; |
| 373 stream->QueueNextPacket(buffer); |
| 342 | 374 |
| 343 stream->QueueNextPacket(buffer); | 375 // QueueNextPacket() can take a long time, especially if several of them |
| 376 // were called back-to-back. Check if we are stopping now. |
| 377 if (stream->state_ != PCMA_PLAYING) |
| 378 return; |
| 344 | 379 |
| 345 // Time to send the buffer to the audio driver. Since we are reusing | 380 // Time to send the buffer to the audio driver. Since we are reusing |
| 346 // the same buffers we can get away without calling waveOutPrepareHeader. | 381 // the same buffers we can get away without calling waveOutPrepareHeader. |
| 347 MMRESULT result = ::waveOutWrite(hwo, buffer, sizeof(WAVEHDR)); | 382 MMRESULT result = ::waveOutWrite(stream->waveout_, |
| 383 buffer, |
| 384 sizeof(WAVEHDR)); |
| 348 if (result != MMSYSERR_NOERROR) | 385 if (result != MMSYSERR_NOERROR) |
| 349 stream->HandleError(result); | 386 stream->HandleError(result); |
| 350 | |
| 351 stream->pending_bytes_ += buffer->dwBufferLength; | 387 stream->pending_bytes_ += buffer->dwBufferLength; |
| 352 ::LeaveCriticalSection(&stream->lock_); | |
| 353 } | 388 } |
| 354 } | 389 } |
| 355 } | 390 } |
| OLD | NEW |