Chromium Code Reviews| 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 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 65 // - It is recommended to first acquire the native sample rate of the default | 65 // - 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 | 66 // input device and then use the same rate when creating this object. Use |
| 67 // WASAPIAudioOutputStream::HardwareSampleRate() to retrieve the sample rate. | 67 // WASAPIAudioOutputStream::HardwareSampleRate() to retrieve the sample rate. |
| 68 // - Calling Close() also leads to self destruction. | 68 // - Calling Close() also leads to self destruction. |
| 69 // - Stream switching is not supported if the user shifts the audio device | 69 // - Stream switching is not supported if the user shifts the audio device |
| 70 // after Open() is called but before Start() has been called. | 70 // after Open() is called but before Start() has been called. |
| 71 // - Stream switching can fail if streaming starts on one device with a | 71 // - Stream switching can fail if streaming starts on one device with a |
| 72 // supported format (X) and the new default device - to which we would like | 72 // supported format (X) and the new default device - to which we would like |
| 73 // to switch - uses another format (Y), which is not supported given the | 73 // to switch - uses another format (Y), which is not supported given the |
| 74 // configured audio parameters. | 74 // configured audio parameters. |
| 75 // - The audio device is always opened with the same number of channels as | |
| 76 // it supports natively (see HardwareChannelCount()). Channel up-mixing will | |
| 77 // take place if the |params| parameter in the constructor contains a lower | |
| 78 // number of channels than the number of native channels. As an example: if | |
| 79 // the clients provides a channel count of 2 and a 7.1 headset is detected, | |
| 80 // then 2 -> 7.1 up-mixing will take place for each OnMoreData() callback. | |
|
scherkus (not reviewing)
2012/08/02 17:14:14
what about opening up 8 channels when HardwareChan
henrika (OOO until Aug 14)
2012/08/03 14:55:56
Added comment about that we don't support it.
Als
| |
| 75 // | 81 // |
| 76 // Core Audio API details: | 82 // Core Audio API details: |
| 77 // | 83 // |
| 78 // - CoInitializeEx() is called on the creating thread and on the internal | 84 // - CoInitializeEx() is called on the creating thread and on the internal |
| 79 // capture thread. Each thread's concurrency model and apartment is set | 85 // capture thread. Each thread's concurrency model and apartment is set |
| 80 // to multi-threaded (MTA). CHECK() is called to ensure that we crash if | 86 // to multi-threaded (MTA). CHECK() is called to ensure that we crash if |
| 81 // CoInitializeEx(MTA) fails. | 87 // CoInitializeEx(MTA) fails. |
| 82 // - The public API methods (Open(), Start(), Stop() and Close()) must be | 88 // - The public API methods (Open(), Start(), Stop() and Close()) must be |
| 83 // called on constructing thread. The reason is that we want to ensure that | 89 // called on constructing thread. The reason is that we want to ensure that |
| 84 // the COM environment is the same for all API implementations. | 90 // the COM environment is the same for all API implementations. |
| (...skipping 23 matching lines...) Expand all Loading... | |
| 108 // - Audio rendering is performed on the audio render thread, owned by this | 114 // - Audio rendering is performed on the audio render thread, owned by this |
| 109 // class, and the AudioSourceCallback::OnMoreData() method will be called | 115 // class, and the AudioSourceCallback::OnMoreData() method will be called |
| 110 // from this thread. Stream switching also takes place on the audio-render | 116 // from this thread. Stream switching also takes place on the audio-render |
| 111 // thread. | 117 // thread. |
| 112 // - All callback methods from the IMMNotificationClient interface will be | 118 // - All callback methods from the IMMNotificationClient interface will be |
| 113 // called on a Windows-internal MMDevice thread. | 119 // called on a Windows-internal MMDevice thread. |
| 114 // | 120 // |
| 115 // Experimental exclusive mode: | 121 // Experimental exclusive mode: |
| 116 // | 122 // |
| 117 // - It is possible to open up a stream in exclusive mode by using the | 123 // - It is possible to open up a stream in exclusive mode by using the |
| 118 // --enable-exclusive-mode command line flag. | 124 // --enable-exclusive-audio command line flag. |
| 119 // - The internal buffering scheme is less flexible for exclusive streams. | 125 // - The internal buffering scheme is less flexible for exclusive streams. |
| 120 // Hence, some manual tuning will be required before deciding what frame | 126 // Hence, some manual tuning will be required before deciding what frame |
| 121 // size to use. See the WinAudioOutputTest unit test for more details. | 127 // size to use. See the WinAudioOutputTest unit test for more details. |
| 122 // - If an application opens a stream in exclusive mode, the application has | 128 // - If an application opens a stream in exclusive mode, the application has |
| 123 // exclusive use of the audio endpoint device that plays the stream. | 129 // exclusive use of the audio endpoint device that plays the stream. |
| 124 // - Exclusive-mode should only be utilized when the lowest possible latency | 130 // - Exclusive-mode should only be utilized when the lowest possible latency |
| 125 // is important. | 131 // is important. |
| 126 // - In exclusive mode, the client can choose to open the stream in any audio | 132 // - In exclusive mode, the client can choose to open the stream in any audio |
| 127 // format that the endpoint device supports, i.e. not limited to the device's | 133 // format that the endpoint device supports, i.e. not limited to the device's |
| 128 // current (default) configuration. | 134 // current (default) configuration. |
| 129 // - Initial measurements on Windows 7 (HP Z600 workstation) have shown that | 135 // - Initial measurements on Windows 7 (HP Z600 workstation) have shown that |
| 130 // the lowest possible latencies we can achieve on this machine are: | 136 // the lowest possible latencies we can achieve on this machine are: |
| 131 // o ~3.3333ms @ 48kHz <=> 160 audio frames per buffer. | 137 // o ~3.3333ms @ 48kHz <=> 160 audio frames per buffer. |
| 132 // o ~3.6281ms @ 44.1kHz <=> 160 audio frames per buffer. | 138 // o ~3.6281ms @ 44.1kHz <=> 160 audio frames per buffer. |
| 133 // - See http://msdn.microsoft.com/en-us/library/windows/desktop/dd370844(v=vs.8 5).aspx | 139 // - See http://msdn.microsoft.com/en-us/library/windows/desktop/dd370844(v=vs.8 5).aspx |
| 134 // for more details. | 140 // for more details. |
| 135 | 141 |
| 136 #ifndef MEDIA_AUDIO_WIN_AUDIO_LOW_LATENCY_OUTPUT_WIN_H_ | 142 #ifndef MEDIA_AUDIO_WIN_AUDIO_LOW_LATENCY_OUTPUT_WIN_H_ |
| 137 #define MEDIA_AUDIO_WIN_AUDIO_LOW_LATENCY_OUTPUT_WIN_H_ | 143 #define MEDIA_AUDIO_WIN_AUDIO_LOW_LATENCY_OUTPUT_WIN_H_ |
| 138 | 144 |
| 139 #include <Audioclient.h> | 145 #include <Audioclient.h> |
| 140 #include <audiopolicy.h> | 146 #include <audiopolicy.h> |
| 141 #include <MMDeviceAPI.h> | 147 #include <MMDeviceAPI.h> |
| 142 | 148 |
| 143 #include <string> | 149 #include <string> |
| 144 | 150 |
| 145 #include "base/compiler_specific.h" | 151 #include "base/compiler_specific.h" |
| 152 #include "base/gtest_prod_util.h" | |
| 146 #include "base/threading/platform_thread.h" | 153 #include "base/threading/platform_thread.h" |
| 147 #include "base/threading/simple_thread.h" | 154 #include "base/threading/simple_thread.h" |
| 148 #include "base/win/scoped_co_mem.h" | 155 #include "base/win/scoped_co_mem.h" |
| 149 #include "base/win/scoped_com_initializer.h" | 156 #include "base/win/scoped_com_initializer.h" |
| 150 #include "base/win/scoped_comptr.h" | 157 #include "base/win/scoped_comptr.h" |
| 151 #include "base/win/scoped_handle.h" | 158 #include "base/win/scoped_handle.h" |
| 152 #include "media/audio/audio_io.h" | 159 #include "media/audio/audio_io.h" |
| 153 #include "media/audio/audio_parameters.h" | 160 #include "media/audio/audio_parameters.h" |
| 154 #include "media/base/media_export.h" | 161 #include "media/base/media_export.h" |
| 155 | 162 |
| 156 namespace media { | 163 namespace media { |
| 157 | 164 |
| 158 class AudioManagerWin; | 165 class AudioManagerWin; |
| 166 class WASAPIAudioOutputStreamTest; | |
|
scherkus (not reviewing)
2012/08/02 17:14:14
AFAIK you don't need to fwd decl this for FRIEND_T
henrika (OOO until Aug 14)
2012/08/03 14:55:56
Correct. Thanks ;-)
| |
| 159 | 167 |
| 160 // AudioOutputStream implementation using Windows Core Audio APIs. | 168 // AudioOutputStream implementation using Windows Core Audio APIs. |
| 161 // The IMMNotificationClient interface enables device event notifications | 169 // The IMMNotificationClient interface enables device event notifications |
| 162 // related to changes in the status of an audio endpoint device. | 170 // related to changes in the status of an audio endpoint device. |
| 163 class MEDIA_EXPORT WASAPIAudioOutputStream | 171 class MEDIA_EXPORT WASAPIAudioOutputStream |
| 164 : public IMMNotificationClient, | 172 : public IMMNotificationClient, |
| 165 public AudioOutputStream, | 173 public AudioOutputStream, |
| 166 public base::DelegateSimpleThread::Delegate { | 174 public base::DelegateSimpleThread::Delegate { |
| 167 public: | 175 public: |
| 168 // The ctor takes all the usual parameters, plus |manager| which is the | 176 // The ctor takes all the usual parameters, plus |manager| which is the |
| 169 // the audio manager who is creating this object. | 177 // the audio manager who is creating this object. |
| 170 WASAPIAudioOutputStream(AudioManagerWin* manager, | 178 WASAPIAudioOutputStream(AudioManagerWin* manager, |
| 171 const AudioParameters& params, | 179 const AudioParameters& params, |
| 172 ERole device_role); | 180 ERole device_role); |
| 173 // The dtor is typically called by the AudioManager only and it is usually | 181 // The dtor is typically called by the AudioManager only and it is usually |
| 174 // triggered by calling AudioOutputStream::Close(). | 182 // triggered by calling AudioOutputStream::Close(). |
| 175 virtual ~WASAPIAudioOutputStream(); | 183 virtual ~WASAPIAudioOutputStream(); |
| 176 | 184 |
| 177 // Implementation of AudioOutputStream. | 185 // Implementation of AudioOutputStream. |
| 178 virtual bool Open() OVERRIDE; | 186 virtual bool Open() OVERRIDE; |
| 179 virtual void Start(AudioSourceCallback* callback) OVERRIDE; | 187 virtual void Start(AudioSourceCallback* callback) OVERRIDE; |
| 180 virtual void Stop() OVERRIDE; | 188 virtual void Stop() OVERRIDE; |
| 181 virtual void Close() OVERRIDE; | 189 virtual void Close() OVERRIDE; |
| 182 virtual void SetVolume(double volume) OVERRIDE; | 190 virtual void SetVolume(double volume) OVERRIDE; |
| 183 virtual void GetVolume(double* volume) OVERRIDE; | 191 virtual void GetVolume(double* volume) OVERRIDE; |
| 184 | 192 |
| 185 // Retrieves the stream format that the audio engine uses for its internal | 193 // Retrieves the number of channels the audio engine uses for its internal |
| 186 // processing/mixing of shared-mode streams. | 194 // processing/mixing of shared-mode streams for the default endpoint device. |
| 187 // This method should not be used in combination with exclusive-mode streams. | 195 static int HardwareChannelCount(); |
| 196 | |
| 197 // Retrieves the channel layout the audio engine uses for its internal | |
| 198 // processing/mixing of shared-mode streams for the default endpoint device. | |
| 199 // Note that we convert an internal channel layout mask (see ChannelMask()) | |
| 200 // into a Chrome-specific channel layout enumerator in this method, hence | |
| 201 // the match might not be perfect. | |
| 202 static ChannelLayout HardwareChannelLayout(); | |
| 203 | |
| 204 // Retrieves the sample rate the audio engine uses for its internal | |
| 205 // processing/mixing of shared-mode streams for the default endpoint device. | |
| 188 static int HardwareSampleRate(ERole device_role); | 206 static int HardwareSampleRate(ERole device_role); |
| 189 | 207 |
| 190 // Returns AUDCLNT_SHAREMODE_EXCLUSIVE if --enable-exclusive-mode is used | 208 // Returns AUDCLNT_SHAREMODE_EXCLUSIVE if --enable-exclusive-mode is used |
| 191 // as command-line flag and AUDCLNT_SHAREMODE_SHARED otherwise (default). | 209 // as command-line flag and AUDCLNT_SHAREMODE_SHARED otherwise (default). |
| 192 static AUDCLNT_SHAREMODE GetShareMode(); | 210 static AUDCLNT_SHAREMODE GetShareMode(); |
| 193 | 211 |
| 194 bool started() const { return started_; } | 212 bool started() const { return started_; } |
| 195 | 213 |
| 196 private: | 214 private: |
| 215 FRIEND_TEST_ALL_PREFIXES(WASAPIAudioOutputStreamTest, HardwareChannelCount); | |
| 216 | |
| 197 // Implementation of IUnknown (trivial in this case). See | 217 // Implementation of IUnknown (trivial in this case). See |
| 198 // msdn.microsoft.com/en-us/library/windows/desktop/dd371403(v=vs.85).aspx | 218 // msdn.microsoft.com/en-us/library/windows/desktop/dd371403(v=vs.85).aspx |
| 199 // for details regarding why proper implementations of AddRef(), Release() | 219 // for details regarding why proper implementations of AddRef(), Release() |
| 200 // and QueryInterface() are not needed here. | 220 // and QueryInterface() are not needed here. |
| 201 STDMETHOD_(ULONG, AddRef)(); | 221 STDMETHOD_(ULONG, AddRef)(); |
| 202 STDMETHOD_(ULONG, Release)(); | 222 STDMETHOD_(ULONG, Release)(); |
| 203 STDMETHOD(QueryInterface)(REFIID iid, void** object); | 223 STDMETHOD(QueryInterface)(REFIID iid, void** object); |
| 204 | 224 |
| 205 // Implementation of the abstract interface IMMNotificationClient. | 225 // Implementation of the abstract interface IMMNotificationClient. |
| 206 // Provides notifications when an audio endpoint device is added or removed, | 226 // Provides notifications when an audio endpoint device is added or removed, |
| (...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 248 // Converts unique endpoint ID to user-friendly device name. | 268 // Converts unique endpoint ID to user-friendly device name. |
| 249 std::string GetDeviceName(LPCWSTR device_id) const; | 269 std::string GetDeviceName(LPCWSTR device_id) const; |
| 250 | 270 |
| 251 // Called on the audio render thread when the current audio stream must | 271 // Called on the audio render thread when the current audio stream must |
| 252 // be re-initialized because the default audio device has changed. This | 272 // be re-initialized because the default audio device has changed. This |
| 253 // method: stops the current renderer, releases and re-creates all WASAPI | 273 // method: stops the current renderer, releases and re-creates all WASAPI |
| 254 // interfaces, creates a new IMMDevice and re-starts rendering using the | 274 // interfaces, creates a new IMMDevice and re-starts rendering using the |
| 255 // new default audio device. | 275 // new default audio device. |
| 256 bool RestartRenderingUsingNewDefaultDevice(); | 276 bool RestartRenderingUsingNewDefaultDevice(); |
| 257 | 277 |
| 258 AUDCLNT_SHAREMODE share_mode() const { return share_mode_; } | 278 // Returns the number of channels the audio engine uses for its internal |
| 279 // processing/mixing of shared-mode streams for the default endpoint device. | |
| 280 int endpoint_channel_count() { return format_.Format.nChannels; } | |
| 281 | |
| 282 // The ratio between the the number of native audio channels used by the | |
| 283 // audio device and the number of audio channels from the client. | |
| 284 int channel_factor() const { | |
| 285 return (format_.Format.nChannels / client_channel_count_); | |
| 286 } | |
| 259 | 287 |
| 260 // Initializes the COM library for use by the calling thread and sets the | 288 // Initializes the COM library for use by the calling thread and sets the |
| 261 // thread's concurrency model to multi-threaded. | 289 // thread's concurrency model to multi-threaded. |
| 262 base::win::ScopedCOMInitializer com_init_; | 290 base::win::ScopedCOMInitializer com_init_; |
| 263 | 291 |
| 264 // Contains the thread ID of the creating thread. | 292 // Contains the thread ID of the creating thread. |
| 265 base::PlatformThreadId creating_thread_id_; | 293 base::PlatformThreadId creating_thread_id_; |
| 266 | 294 |
| 267 // Our creator, the audio manager needs to be notified when we close. | 295 // Our creator, the audio manager needs to be notified when we close. |
| 268 AudioManagerWin* manager_; | 296 AudioManagerWin* manager_; |
| 269 | 297 |
| 270 // Rendering is driven by this thread (which has no message loop). | 298 // Rendering is driven by this thread (which has no message loop). |
| 271 // All OnMoreData() callbacks will be called from this thread. | 299 // All OnMoreData() callbacks will be called from this thread. |
| 272 base::DelegateSimpleThread* render_thread_; | 300 base::DelegateSimpleThread* render_thread_; |
| 273 | 301 |
| 274 // Contains the desired audio format which is set up at construction. | 302 // Contains the desired audio format which is set up at construction. |
| 275 WAVEFORMATEX format_; | 303 // Extended PCM waveform format structure based on WAVEFORMATEXTENSIBLE. |
| 304 // Use this for multiple channel and hi-resolution PCM data. | |
| 305 WAVEFORMATPCMEX format_; | |
| 276 | 306 |
| 277 // Copy of the audio format which we know the audio engine supports. | 307 // Copy of the audio format which we know the audio engine supports. |
| 278 // It is recommended to ensure that the sample rate in |format_| is identical | 308 // It is recommended to ensure that the sample rate in |format_| is identical |
| 279 // to the sample rate in |audio_engine_mix_format_|. | 309 // to the sample rate in |audio_engine_mix_format_|. |
| 280 base::win::ScopedCoMem<WAVEFORMATEX> audio_engine_mix_format_; | 310 base::win::ScopedCoMem<WAVEFORMATPCMEX> audio_engine_mix_format_; |
| 281 | 311 |
| 282 bool opened_; | 312 bool opened_; |
| 283 bool started_; | 313 bool started_; |
| 284 | 314 |
| 285 // Set to true as soon as a new default device is detected, and cleared when | 315 // Set to true as soon as a new default device is detected, and cleared when |
| 286 // the streaming has switched from using the old device to the new device. | 316 // the streaming has switched from using the old device to the new device. |
| 287 // All additional device detections during an active state are ignored to | 317 // All additional device detections during an active state are ignored to |
| 288 // ensure that the ongoing switch can finalize without disruptions. | 318 // ensure that the ongoing switch can finalize without disruptions. |
| 289 bool restart_rendering_mode_; | 319 bool restart_rendering_mode_; |
| 290 | 320 |
| (...skipping 18 matching lines...) Expand all Loading... | |
| 309 size_t endpoint_buffer_size_frames_; | 339 size_t endpoint_buffer_size_frames_; |
| 310 | 340 |
| 311 // Defines the role that the system has assigned to an audio endpoint device. | 341 // Defines the role that the system has assigned to an audio endpoint device. |
| 312 ERole device_role_; | 342 ERole device_role_; |
| 313 | 343 |
| 314 // The sharing mode for the connection. | 344 // The sharing mode for the connection. |
| 315 // Valid values are AUDCLNT_SHAREMODE_SHARED and AUDCLNT_SHAREMODE_EXCLUSIVE | 345 // Valid values are AUDCLNT_SHAREMODE_SHARED and AUDCLNT_SHAREMODE_EXCLUSIVE |
| 316 // where AUDCLNT_SHAREMODE_SHARED is the default. | 346 // where AUDCLNT_SHAREMODE_SHARED is the default. |
| 317 AUDCLNT_SHAREMODE share_mode_; | 347 AUDCLNT_SHAREMODE share_mode_; |
| 318 | 348 |
| 349 // The channel count set by the client in |params| which is provided to the | |
| 350 // constructor. The client must feed the AudioSourceCallback::OnMoreData() | |
| 351 // callback with PCM-data that contains this number of channels. | |
| 352 int client_channel_count_; | |
| 353 | |
| 319 // Counts the number of audio frames written to the endpoint buffer. | 354 // Counts the number of audio frames written to the endpoint buffer. |
| 320 UINT64 num_written_frames_; | 355 UINT64 num_written_frames_; |
| 321 | 356 |
| 322 // Pointer to the client that will deliver audio samples to be played out. | 357 // Pointer to the client that will deliver audio samples to be played out. |
| 323 AudioSourceCallback* source_; | 358 AudioSourceCallback* source_; |
| 324 | 359 |
| 325 // An IMMDeviceEnumerator interface which represents a device enumerator. | 360 // An IMMDeviceEnumerator interface which represents a device enumerator. |
| 326 base::win::ScopedComPtr<IMMDeviceEnumerator> device_enumerator_; | 361 base::win::ScopedComPtr<IMMDeviceEnumerator> device_enumerator_; |
| 327 | 362 |
| 328 // An IMMDevice interface which represents an audio endpoint device. | 363 // An IMMDevice interface which represents an audio endpoint device. |
| (...skipping 16 matching lines...) Expand all Loading... | |
| 345 | 380 |
| 346 // This event will be signaled when stream switching shall take place. | 381 // This event will be signaled when stream switching shall take place. |
| 347 base::win::ScopedHandle stream_switch_event_; | 382 base::win::ScopedHandle stream_switch_event_; |
| 348 | 383 |
| 349 DISALLOW_COPY_AND_ASSIGN(WASAPIAudioOutputStream); | 384 DISALLOW_COPY_AND_ASSIGN(WASAPIAudioOutputStream); |
| 350 }; | 385 }; |
| 351 | 386 |
| 352 } // namespace media | 387 } // namespace media |
| 353 | 388 |
| 354 #endif // MEDIA_AUDIO_WIN_AUDIO_LOW_LATENCY_OUTPUT_WIN_H_ | 389 #endif // MEDIA_AUDIO_WIN_AUDIO_LOW_LATENCY_OUTPUT_WIN_H_ |
| OLD | NEW |