| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 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 // Implementation of AudioOutputStream for Windows using Windows Core Audio | 5 // Implementation of AudioOutputStream for Windows using Windows Core Audio |
| 6 // WASAPI for low latency rendering. | 6 // WASAPI for low latency rendering. |
| 7 // | 7 // |
| 8 // Overview of operation and performance: | 8 // Overview of operation and performance: |
| 9 // | 9 // |
| 10 // - An object of WASAPIAudioOutputStream is created by the AudioManager | 10 // - An object of WASAPIAudioOutputStream is created by the AudioManager |
| (...skipping 14 matching lines...) Expand all Loading... |
| 25 // latency of approximately 35 ms if the selected packet size is less than | 25 // latency of approximately 35 ms if the selected packet size is less than |
| 26 // or equal to 20 ms. Using a packet size of 10 ms does not result in a | 26 // or equal to 20 ms. Using a packet size of 10 ms does not result in a |
| 27 // lower latency but only affects the size of the data buffer in each | 27 // lower latency but only affects the size of the data buffer in each |
| 28 // OnMoreData() callback. | 28 // OnMoreData() callback. |
| 29 // - A total typical delay of 35 ms contains three parts: | 29 // - A total typical delay of 35 ms contains three parts: |
| 30 // o Audio endpoint device period (~10 ms). | 30 // o Audio endpoint device period (~10 ms). |
| 31 // o Stream latency between the buffer and endpoint device (~5 ms). | 31 // o Stream latency between the buffer and endpoint device (~5 ms). |
| 32 // o Endpoint buffer (~20 ms to ensure glitch-free rendering). | 32 // o Endpoint buffer (~20 ms to ensure glitch-free rendering). |
| 33 // - Note that, if the user selects a packet size of e.g. 100 ms, the total | 33 // - Note that, if the user selects a packet size of e.g. 100 ms, the total |
| 34 // delay will be approximately 115 ms (10 + 5 + 100). | 34 // delay will be approximately 115 ms (10 + 5 + 100). |
| 35 // - Supports device events using the IMMNotificationClient Interface. If | |
| 36 // streaming has started, a so-called stream switch will take place in the | |
| 37 // following situations: | |
| 38 // o The user enables or disables an audio endpoint device from Device | |
| 39 // Manager or from the Windows multimedia control panel, Mmsys.cpl. | |
| 40 // o The user adds an audio adapter to the system or removes an audio | |
| 41 // adapter from the system. | |
| 42 // o The user plugs an audio endpoint device into an audio jack with | |
| 43 // jack-presence detection, or removes an audio endpoint device from | |
| 44 // such a jack. | |
| 45 // o The user changes the device role that is assigned to a device. | |
| 46 // o The value of a property of a device changes. | |
| 47 // Practical/typical example: A user has two audio devices A and B where | |
| 48 // A is a built-in device configured as Default Communication and B is a | |
| 49 // USB device set as Default device. Audio rendering starts and audio is | |
| 50 // played through the device B since the eConsole role is used by the audio | |
| 51 // manager in Chrome today. If the user now removes the USB device (B), it | |
| 52 // will be detected and device A will instead be defined as the new default | |
| 53 // device. Rendering will automatically stop, all resources will be released | |
| 54 // and a new session will be initialized and started using device A instead. | |
| 55 // The net effect for the user is that audio will automatically switch from | |
| 56 // device B to device A. Same thing will happen if the user now re-inserts | |
| 57 // the USB device again. | |
| 58 // | 35 // |
| 59 // Implementation notes: | 36 // Implementation notes: |
| 60 // | 37 // |
| 61 // - The minimum supported client is Windows Vista. | 38 // - The minimum supported client is Windows Vista. |
| 62 // - This implementation is single-threaded, hence: | 39 // - This implementation is single-threaded, hence: |
| 63 // o Construction and destruction must take place from the same thread. | 40 // o Construction and destruction must take place from the same thread. |
| 64 // o All APIs must be called from the creating thread as well. | 41 // o All APIs must be called from the creating thread as well. |
| 65 // - It is recommended to first acquire the native sample rate of the default | 42 // - It is recommended to first acquire the native sample rate of the default |
| 66 // input device and then use the same rate when creating this object. Use | 43 // input device and then use the same rate when creating this object. Use |
| 67 // WASAPIAudioOutputStream::HardwareSampleRate() to retrieve the sample rate. | 44 // WASAPIAudioOutputStream::HardwareSampleRate() to retrieve the sample rate. |
| (...skipping 19 matching lines...) Expand all Loading... |
| 87 // - Utilized WASAPI interfaces: | 64 // - Utilized WASAPI interfaces: |
| 88 // o IAudioClient | 65 // o IAudioClient |
| 89 // o IAudioRenderClient | 66 // o IAudioRenderClient |
| 90 // - The stream is initialized in shared mode and the processing of the | 67 // - The stream is initialized in shared mode and the processing of the |
| 91 // audio buffer is event driven. | 68 // audio buffer is event driven. |
| 92 // - The Multimedia Class Scheduler service (MMCSS) is utilized to boost | 69 // - The Multimedia Class Scheduler service (MMCSS) is utilized to boost |
| 93 // the priority of the render thread. | 70 // the priority of the render thread. |
| 94 // - Audio-rendering endpoint devices can have three roles: | 71 // - Audio-rendering endpoint devices can have three roles: |
| 95 // Console (eConsole), Communications (eCommunications), and Multimedia | 72 // Console (eConsole), Communications (eCommunications), and Multimedia |
| 96 // (eMultimedia). Search for "Device Roles" on MSDN for more details. | 73 // (eMultimedia). Search for "Device Roles" on MSDN for more details. |
| 97 // - The actual stream-switch is executed on the audio-render thread but it | |
| 98 // is triggered by an internal MMDevice thread using callback methods | |
| 99 // in the IMMNotificationClient interface. | |
| 100 // | 74 // |
| 101 // Threading details: | 75 // Threading details: |
| 102 // | 76 // |
| 103 // - It is assumed that this class is created on the audio thread owned | 77 // - It is assumed that this class is created on the audio thread owned |
| 104 // by the AudioManager. | 78 // by the AudioManager. |
| 105 // - It is a requirement to call the following methods on the same audio | 79 // - It is a requirement to call the following methods on the same audio |
| 106 // thread: Open(), Start(), Stop(), and Close(). | 80 // thread: Open(), Start(), Stop(), and Close(). |
| 107 // - Audio rendering is performed on the audio render thread, owned by this | 81 // - Audio rendering is performed on the audio render thread, owned by this |
| 108 // class, and the AudioSourceCallback::OnMoreData() method will be called | 82 // class, and the AudioSourceCallback::OnMoreData() method will be called |
| 109 // from this thread. Stream switching also takes place on the audio-render | 83 // from this thread. Stream switching also takes place on the audio-render |
| 110 // thread. | 84 // thread. |
| 111 // - All callback methods from the IMMNotificationClient interface will be | |
| 112 // called on a Windows-internal MMDevice thread. | |
| 113 // | 85 // |
| 114 // Experimental exclusive mode: | 86 // Experimental exclusive mode: |
| 115 // | 87 // |
| 116 // - It is possible to open up a stream in exclusive mode by using the | 88 // - It is possible to open up a stream in exclusive mode by using the |
| 117 // --enable-exclusive-audio command line flag. | 89 // --enable-exclusive-audio command line flag. |
| 118 // - The internal buffering scheme is less flexible for exclusive streams. | 90 // - The internal buffering scheme is less flexible for exclusive streams. |
| 119 // Hence, some manual tuning will be required before deciding what frame | 91 // Hence, some manual tuning will be required before deciding what frame |
| 120 // size to use. See the WinAudioOutputTest unit test for more details. | 92 // size to use. See the WinAudioOutputTest unit test for more details. |
| 121 // - If an application opens a stream in exclusive mode, the application has | 93 // - If an application opens a stream in exclusive mode, the application has |
| 122 // exclusive use of the audio endpoint device that plays the stream. | 94 // exclusive use of the audio endpoint device that plays the stream. |
| 123 // - Exclusive-mode should only be utilized when the lowest possible latency | 95 // - Exclusive-mode should only be utilized when the lowest possible latency |
| 124 // is important. | 96 // is important. |
| 125 // - In exclusive mode, the client can choose to open the stream in any audio | 97 // - In exclusive mode, the client can choose to open the stream in any audio |
| 126 // format that the endpoint device supports, i.e. not limited to the device's | 98 // format that the endpoint device supports, i.e. not limited to the device's |
| 127 // current (default) configuration. | 99 // current (default) configuration. |
| 128 // - Initial measurements on Windows 7 (HP Z600 workstation) have shown that | 100 // - Initial measurements on Windows 7 (HP Z600 workstation) have shown that |
| 129 // the lowest possible latencies we can achieve on this machine are: | 101 // the lowest possible latencies we can achieve on this machine are: |
| 130 // o ~3.3333ms @ 48kHz <=> 160 audio frames per buffer. | 102 // o ~3.3333ms @ 48kHz <=> 160 audio frames per buffer. |
| 131 // o ~3.6281ms @ 44.1kHz <=> 160 audio frames per buffer. | 103 // o ~3.6281ms @ 44.1kHz <=> 160 audio frames per buffer. |
| 132 // - See http://msdn.microsoft.com/en-us/library/windows/desktop/dd370844(v=vs.8
5).aspx | 104 // - See http://msdn.microsoft.com/en-us/library/windows/desktop/dd370844(v=vs.8
5).aspx |
| 133 // for more details. | 105 // for more details. |
| 134 | 106 |
| 135 #ifndef MEDIA_AUDIO_WIN_AUDIO_LOW_LATENCY_OUTPUT_WIN_H_ | 107 #ifndef MEDIA_AUDIO_WIN_AUDIO_LOW_LATENCY_OUTPUT_WIN_H_ |
| 136 #define MEDIA_AUDIO_WIN_AUDIO_LOW_LATENCY_OUTPUT_WIN_H_ | 108 #define MEDIA_AUDIO_WIN_AUDIO_LOW_LATENCY_OUTPUT_WIN_H_ |
| 137 | 109 |
| 138 #include <Audioclient.h> | 110 #include <Audioclient.h> |
| 139 #include <audiopolicy.h> | |
| 140 #include <MMDeviceAPI.h> | 111 #include <MMDeviceAPI.h> |
| 141 | 112 |
| 142 #include <string> | 113 #include <string> |
| 143 | 114 |
| 144 #include "base/compiler_specific.h" | 115 #include "base/compiler_specific.h" |
| 145 #include "base/memory/scoped_ptr.h" | 116 #include "base/memory/scoped_ptr.h" |
| 146 #include "base/threading/platform_thread.h" | 117 #include "base/threading/platform_thread.h" |
| 147 #include "base/threading/simple_thread.h" | 118 #include "base/threading/simple_thread.h" |
| 148 #include "base/win/scoped_co_mem.h" | 119 #include "base/win/scoped_co_mem.h" |
| 149 #include "base/win/scoped_com_initializer.h" | 120 #include "base/win/scoped_com_initializer.h" |
| 150 #include "base/win/scoped_comptr.h" | 121 #include "base/win/scoped_comptr.h" |
| 151 #include "base/win/scoped_handle.h" | 122 #include "base/win/scoped_handle.h" |
| 152 #include "media/audio/audio_io.h" | 123 #include "media/audio/audio_io.h" |
| 153 #include "media/audio/audio_parameters.h" | 124 #include "media/audio/audio_parameters.h" |
| 154 #include "media/base/media_export.h" | 125 #include "media/base/media_export.h" |
| 155 | 126 |
| 156 namespace media { | 127 namespace media { |
| 157 | 128 |
| 158 class AudioManagerWin; | 129 class AudioManagerWin; |
| 159 | 130 |
| 160 // AudioOutputStream implementation using Windows Core Audio APIs. | 131 // AudioOutputStream implementation using Windows Core Audio APIs. |
| 161 // TODO(henrika): Remove IMMNotificationClient implementation now that we have | 132 class MEDIA_EXPORT WASAPIAudioOutputStream : |
| 162 // AudioDeviceListenerWin; currently just disabled since extraction is extremely | |
| 163 // advanced. | |
| 164 class MEDIA_EXPORT WASAPIAudioOutputStream | |
| 165 : public IMMNotificationClient, | |
| 166 public AudioOutputStream, | 133 public AudioOutputStream, |
| 167 public base::DelegateSimpleThread::Delegate { | 134 public base::DelegateSimpleThread::Delegate { |
| 168 public: | 135 public: |
| 169 // The ctor takes all the usual parameters, plus |manager| which is the | 136 // The ctor takes all the usual parameters, plus |manager| which is the |
| 170 // the audio manager who is creating this object. | 137 // the audio manager who is creating this object. |
| 171 WASAPIAudioOutputStream(AudioManagerWin* manager, | 138 WASAPIAudioOutputStream(AudioManagerWin* manager, |
| 172 const AudioParameters& params, | 139 const AudioParameters& params, |
| 173 ERole device_role); | 140 ERole device_role); |
| 141 |
| 174 // The dtor is typically called by the AudioManager only and it is usually | 142 // The dtor is typically called by the AudioManager only and it is usually |
| 175 // triggered by calling AudioOutputStream::Close(). | 143 // triggered by calling AudioOutputStream::Close(). |
| 176 virtual ~WASAPIAudioOutputStream(); | 144 virtual ~WASAPIAudioOutputStream(); |
| 177 | 145 |
| 178 // Implementation of AudioOutputStream. | 146 // Implementation of AudioOutputStream. |
| 179 virtual bool Open() OVERRIDE; | 147 virtual bool Open() OVERRIDE; |
| 180 virtual void Start(AudioSourceCallback* callback) OVERRIDE; | 148 virtual void Start(AudioSourceCallback* callback) OVERRIDE; |
| 181 virtual void Stop() OVERRIDE; | 149 virtual void Stop() OVERRIDE; |
| 182 virtual void Close() OVERRIDE; | 150 virtual void Close() OVERRIDE; |
| 183 virtual void SetVolume(double volume) OVERRIDE; | 151 virtual void SetVolume(double volume) OVERRIDE; |
| (...skipping 18 matching lines...) Expand all Loading... |
| 202 // as command-line flag and AUDCLNT_SHAREMODE_SHARED otherwise (default). | 170 // as command-line flag and AUDCLNT_SHAREMODE_SHARED otherwise (default). |
| 203 static AUDCLNT_SHAREMODE GetShareMode(); | 171 static AUDCLNT_SHAREMODE GetShareMode(); |
| 204 | 172 |
| 205 bool started() const { return render_thread_.get() != NULL; } | 173 bool started() const { return render_thread_.get() != NULL; } |
| 206 | 174 |
| 207 // Returns the number of channels the audio engine uses for its internal | 175 // Returns the number of channels the audio engine uses for its internal |
| 208 // processing/mixing of shared-mode streams for the default endpoint device. | 176 // processing/mixing of shared-mode streams for the default endpoint device. |
| 209 int GetEndpointChannelCountForTesting() { return format_.Format.nChannels; } | 177 int GetEndpointChannelCountForTesting() { return format_.Format.nChannels; } |
| 210 | 178 |
| 211 private: | 179 private: |
| 212 // Implementation of IUnknown (trivial in this case). See | |
| 213 // msdn.microsoft.com/en-us/library/windows/desktop/dd371403(v=vs.85).aspx | |
| 214 // for details regarding why proper implementations of AddRef(), Release() | |
| 215 // and QueryInterface() are not needed here. | |
| 216 STDMETHOD_(ULONG, AddRef)(); | |
| 217 STDMETHOD_(ULONG, Release)(); | |
| 218 STDMETHOD(QueryInterface)(REFIID iid, void** object); | |
| 219 | |
| 220 // Implementation of the abstract interface IMMNotificationClient. | |
| 221 // Provides notifications when an audio endpoint device is added or removed, | |
| 222 // when the state or properties of a device change, or when there is a | |
| 223 // change in the default role assigned to a device. See | |
| 224 // msdn.microsoft.com/en-us/library/windows/desktop/dd371417(v=vs.85).aspx | |
| 225 // for more details about the IMMNotificationClient interface. | |
| 226 | |
| 227 // The default audio endpoint device for a particular role has changed. | |
| 228 // This method is only used for diagnostic purposes. | |
| 229 STDMETHOD(OnDeviceStateChanged)(LPCWSTR device_id, DWORD new_state); | |
| 230 | |
| 231 // Indicates that the state of an audio endpoint device has changed. | |
| 232 STDMETHOD(OnDefaultDeviceChanged)(EDataFlow flow, ERole role, | |
| 233 LPCWSTR new_default_device_id); | |
| 234 | |
| 235 // These IMMNotificationClient methods are currently not utilized. | |
| 236 STDMETHOD(OnDeviceAdded)(LPCWSTR device_id) { return S_OK; } | |
| 237 STDMETHOD(OnDeviceRemoved)(LPCWSTR device_id) { return S_OK; } | |
| 238 STDMETHOD(OnPropertyValueChanged)(LPCWSTR device_id, | |
| 239 const PROPERTYKEY key) { | |
| 240 return S_OK; | |
| 241 } | |
| 242 | |
| 243 // DelegateSimpleThread::Delegate implementation. | 180 // DelegateSimpleThread::Delegate implementation. |
| 244 virtual void Run() OVERRIDE; | 181 virtual void Run() OVERRIDE; |
| 245 | 182 |
| 246 // Issues the OnError() callback to the |sink_|. | 183 // Issues the OnError() callback to the |sink_|. |
| 247 void HandleError(HRESULT err); | 184 void HandleError(HRESULT err); |
| 248 | 185 |
| 249 // The Open() method is divided into these sub methods. | 186 // The Open() method is divided into these sub methods. |
| 250 HRESULT SetRenderDevice(); | 187 HRESULT SetRenderDevice(); |
| 251 HRESULT ActivateRenderDevice(); | 188 HRESULT ActivateRenderDevice(); |
| 252 bool DesiredFormatIsSupported(); | 189 bool DesiredFormatIsSupported(); |
| 253 HRESULT InitializeAudioEngine(); | 190 HRESULT InitializeAudioEngine(); |
| 254 | 191 |
| 255 // Called when the device will be opened in shared mode and use the | 192 // Called when the device will be opened in shared mode and use the |
| 256 // internal audio engine's mix format. | 193 // internal audio engine's mix format. |
| 257 HRESULT SharedModeInitialization(); | 194 HRESULT SharedModeInitialization(); |
| 258 | 195 |
| 259 // Called when the device will be opened in exclusive mode and use the | 196 // Called when the device will be opened in exclusive mode and use the |
| 260 // application specified format. | 197 // application specified format. |
| 261 HRESULT ExclusiveModeInitialization(); | 198 HRESULT ExclusiveModeInitialization(); |
| 262 | 199 |
| 263 // Converts unique endpoint ID to user-friendly device name. | 200 // Converts unique endpoint ID to user-friendly device name. |
| 264 std::string GetDeviceName(LPCWSTR device_id) const; | 201 std::string GetDeviceName(LPCWSTR device_id) const; |
| 265 | 202 |
| 266 // Called on the audio render thread when the current audio stream must | |
| 267 // be re-initialized because the default audio device has changed. This | |
| 268 // method: stops the current renderer, releases and re-creates all WASAPI | |
| 269 // interfaces, creates a new IMMDevice and re-starts rendering using the | |
| 270 // new default audio device. | |
| 271 bool RestartRenderingUsingNewDefaultDevice(); | |
| 272 | |
| 273 // Contains the thread ID of the creating thread. | 203 // Contains the thread ID of the creating thread. |
| 274 base::PlatformThreadId creating_thread_id_; | 204 base::PlatformThreadId creating_thread_id_; |
| 275 | 205 |
| 276 // Our creator, the audio manager needs to be notified when we close. | 206 // Our creator, the audio manager needs to be notified when we close. |
| 277 AudioManagerWin* manager_; | 207 AudioManagerWin* manager_; |
| 278 | 208 |
| 279 // Rendering is driven by this thread (which has no message loop). | 209 // Rendering is driven by this thread (which has no message loop). |
| 280 // All OnMoreData() callbacks will be called from this thread. | 210 // All OnMoreData() callbacks will be called from this thread. |
| 281 scoped_ptr<base::DelegateSimpleThread> render_thread_; | 211 scoped_ptr<base::DelegateSimpleThread> render_thread_; |
| 282 | 212 |
| (...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 351 // data to a rendering endpoint buffer. | 281 // data to a rendering endpoint buffer. |
| 352 base::win::ScopedComPtr<IAudioRenderClient> audio_render_client_; | 282 base::win::ScopedComPtr<IAudioRenderClient> audio_render_client_; |
| 353 | 283 |
| 354 // The audio engine will signal this event each time a buffer becomes | 284 // The audio engine will signal this event each time a buffer becomes |
| 355 // ready to be filled by the client. | 285 // ready to be filled by the client. |
| 356 base::win::ScopedHandle audio_samples_render_event_; | 286 base::win::ScopedHandle audio_samples_render_event_; |
| 357 | 287 |
| 358 // This event will be signaled when rendering shall stop. | 288 // This event will be signaled when rendering shall stop. |
| 359 base::win::ScopedHandle stop_render_event_; | 289 base::win::ScopedHandle stop_render_event_; |
| 360 | 290 |
| 361 // This event will be signaled when stream switching shall take place. | |
| 362 base::win::ScopedHandle stream_switch_event_; | |
| 363 | |
| 364 // Container for retrieving data from AudioSourceCallback::OnMoreData(). | 291 // Container for retrieving data from AudioSourceCallback::OnMoreData(). |
| 365 scoped_ptr<AudioBus> audio_bus_; | 292 scoped_ptr<AudioBus> audio_bus_; |
| 366 | 293 |
| 367 DISALLOW_COPY_AND_ASSIGN(WASAPIAudioOutputStream); | 294 DISALLOW_COPY_AND_ASSIGN(WASAPIAudioOutputStream); |
| 368 }; | 295 }; |
| 369 | 296 |
| 370 } // namespace media | 297 } // namespace media |
| 371 | 298 |
| 372 #endif // MEDIA_AUDIO_WIN_AUDIO_LOW_LATENCY_OUTPUT_WIN_H_ | 299 #endif // MEDIA_AUDIO_WIN_AUDIO_LOW_LATENCY_OUTPUT_WIN_H_ |
| OLD | NEW |