| 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 // Creates an output stream based on the ALSA PCM interface. | 5 // Creates an output stream based on the ALSA PCM interface. |
| 6 // | 6 // |
| 7 // On device write failure, the stream will move itself to an invalid state. | 7 // On device write failure, the stream will move itself to an invalid state. |
| 8 // No more data will be pulled from the data source, or written to the device. | 8 // No more data will be pulled from the data source, or written to the device. |
| 9 // All calls to public API functions will either no-op themselves, or return an | 9 // All calls to public API functions will either no-op themselves, or return an |
| 10 // error if possible. Specifically, If the stream is in an error state, Open() | 10 // error if possible. Specifically, If the stream is in an error state, Open() |
| 11 // will return false, and Start() will call OnError() immediately on the | 11 // will return false, and Start() will call OnError() immediately on the |
| 12 // provided callback. | 12 // provided callback. |
| 13 // | 13 // |
| 14 // TODO(ajwong): The OnClose() and OnError() calling needing fixing. | 14 // TODO(ajwong): The OnClose() and OnError() calling needing fixing. |
| 15 // | 15 // |
| 16 // If the stream is successfully opened, Close() must be called before the | 16 // If the stream is successfully opened, Close() must be called before the |
| 17 // stream is deleted as Close() is responsible for ensuring resource cleanup | 17 // stream is deleted as Close() is responsible for ensuring resource cleanup |
| 18 // occurs. | 18 // occurs. |
| 19 // | 19 // |
| 20 // This object's thread-safety is a little tricky. This object's public API | 20 // This object's thread-safety is a little tricky. This object's public API |
| 21 // can only be called from the thread that created the object. Calling the | 21 // can only be called from the thread that created the object. Calling the |
| 22 // public APIs in any method that may cause concurrent execution will result in | 22 // public APIs in any method that may cause concurrent execution will result in |
| 23 // a race condition. When modifying the code in this class, please read the | 23 // a race condition. When modifying the code in this class, please read the |
| 24 // threading assumptions at the top of the implementation file to avoid | 24 // threading assumptions at the top of the implementation file to avoid |
| 25 // introducing race conditions between tasks posted to the internal | 25 // introducing race conditions between tasks posted to the internal |
| 26 // message_loop, and the thread calling the public APIs. | 26 // message_loop, and the thread calling the public APIs. |
| 27 // | |
| 28 // TODO(sergeyu): AlsaPcmOutputStream is always created and used from the | |
| 29 // audio thread (i.e. |client_thread_loop_| and |message_loop_| always point | |
| 30 // to the same thread), so it doesn't need to be thread-safe anymore. | |
| 31 // | |
| 32 // TODO(sergeyu): Remove refcounter from AlsaPcmOutputStream and use | |
| 33 // ScopedRunnableMethodFactory to create tasks. | |
| 34 | 27 |
| 35 #ifndef MEDIA_AUDIO_LINUX_ALSA_OUTPUT_H_ | 28 #ifndef MEDIA_AUDIO_LINUX_ALSA_OUTPUT_H_ |
| 36 #define MEDIA_AUDIO_LINUX_ALSA_OUTPUT_H_ | 29 #define MEDIA_AUDIO_LINUX_ALSA_OUTPUT_H_ |
| 37 | 30 |
| 38 #include <alsa/asoundlib.h> | 31 #include <alsa/asoundlib.h> |
| 39 | 32 |
| 40 #include <string> | 33 #include <string> |
| 41 | 34 |
| 42 #include "base/gtest_prod_util.h" | 35 #include "base/gtest_prod_util.h" |
| 43 #include "base/memory/ref_counted.h" | |
| 44 #include "base/memory/scoped_ptr.h" | 36 #include "base/memory/scoped_ptr.h" |
| 45 #include "base/synchronization/lock.h" | 37 #include "base/task.h" |
| 46 #include "media/audio/audio_io.h" | 38 #include "media/audio/audio_io.h" |
| 47 #include "media/audio/audio_parameters.h" | 39 #include "media/audio/audio_parameters.h" |
| 48 | 40 |
| 49 namespace media { | 41 namespace media { |
| 50 class SeekableBuffer; | 42 class SeekableBuffer; |
| 51 }; // namespace media | 43 }; // namespace media |
| 52 | 44 |
| 53 class AlsaWrapper; | 45 class AlsaWrapper; |
| 54 class AudioManagerLinux; | 46 class AudioManagerLinux; |
| 55 class MessageLoop; | 47 class MessageLoop; |
| 56 | 48 |
| 57 class AlsaPcmOutputStream : | 49 class AlsaPcmOutputStream : public AudioOutputStream { |
| 58 public AudioOutputStream, | |
| 59 public base::RefCountedThreadSafe<AlsaPcmOutputStream> { | |
| 60 public: | 50 public: |
| 61 // String for the generic "default" ALSA device that has the highest | 51 // String for the generic "default" ALSA device that has the highest |
| 62 // compatibility and chance of working. | 52 // compatibility and chance of working. |
| 63 static const char kDefaultDevice[]; | 53 static const char kDefaultDevice[]; |
| 64 | 54 |
| 65 // Pass this to the AlsaPcmOutputStream if you want to attempt auto-selection | 55 // Pass this to the AlsaPcmOutputStream if you want to attempt auto-selection |
| 66 // of the audio device. | 56 // of the audio device. |
| 67 static const char kAutoSelectDevice[]; | 57 static const char kAutoSelectDevice[]; |
| 68 | 58 |
| 69 // Prefix for device names to enable ALSA library resampling. | 59 // Prefix for device names to enable ALSA library resampling. |
| 70 static const char kPlugPrefix[]; | 60 static const char kPlugPrefix[]; |
| 71 | 61 |
| 72 // The minimum latency that is accepted by the device. | 62 // The minimum latency that is accepted by the device. |
| 73 static const uint32 kMinLatencyMicros; | 63 static const uint32 kMinLatencyMicros; |
| 74 | 64 |
| 75 // Create a PCM Output stream for the ALSA device identified by | 65 // Create a PCM Output stream for the ALSA device identified by |
| 76 // |device_name|. The AlsaPcmOutputStream uses |wrapper| to communicate with | 66 // |device_name|. The AlsaPcmOutputStream uses |wrapper| to communicate with |
| 77 // the alsa libraries, allowing for dependency injection during testing. All | 67 // the alsa libraries, allowing for dependency injection during testing. All |
| 78 // requesting of data, and writing to the alsa device will be done on | 68 // requesting of data, and writing to the alsa device will be done on |
| 79 // |message_loop|. | 69 // |message_loop|. |
| 80 // | 70 // |
| 81 // If unsure of what to use for |device_name|, use |kAutoSelectDevice|. | 71 // If unsure of what to use for |device_name|, use |kAutoSelectDevice|. |
| 82 AlsaPcmOutputStream(const std::string& device_name, | 72 AlsaPcmOutputStream(const std::string& device_name, |
| 83 AudioParameters params, | 73 AudioParameters params, |
| 84 AlsaWrapper* wrapper, | 74 AlsaWrapper* wrapper, |
| 85 AudioManagerLinux* manager, | 75 AudioManagerLinux* manager, |
| 86 MessageLoop* message_loop); | 76 MessageLoop* message_loop); |
| 87 | 77 |
| 78 virtual ~AlsaPcmOutputStream(); |
| 79 |
| 88 // Implementation of AudioOutputStream. | 80 // Implementation of AudioOutputStream. |
| 89 virtual bool Open(); | 81 virtual bool Open(); |
| 90 virtual void Close(); | 82 virtual void Close(); |
| 91 virtual void Start(AudioSourceCallback* callback); | 83 virtual void Start(AudioSourceCallback* callback); |
| 92 virtual void Stop(); | 84 virtual void Stop(); |
| 93 virtual void SetVolume(double volume); | 85 virtual void SetVolume(double volume); |
| 94 virtual void GetVolume(double* volume); | 86 virtual void GetVolume(double* volume); |
| 95 | 87 |
| 96 private: | 88 private: |
| 97 friend class base::RefCountedThreadSafe<AlsaPcmOutputStream>; | |
| 98 friend class AlsaPcmOutputStreamTest; | 89 friend class AlsaPcmOutputStreamTest; |
| 99 FRIEND_TEST_ALL_PREFIXES(AlsaPcmOutputStreamTest, | 90 FRIEND_TEST_ALL_PREFIXES(AlsaPcmOutputStreamTest, |
| 100 AutoSelectDevice_DeviceSelect); | 91 AutoSelectDevice_DeviceSelect); |
| 101 FRIEND_TEST_ALL_PREFIXES(AlsaPcmOutputStreamTest, | 92 FRIEND_TEST_ALL_PREFIXES(AlsaPcmOutputStreamTest, |
| 102 AutoSelectDevice_FallbackDevices); | 93 AutoSelectDevice_FallbackDevices); |
| 103 FRIEND_TEST_ALL_PREFIXES(AlsaPcmOutputStreamTest, AutoSelectDevice_HintFail); | 94 FRIEND_TEST_ALL_PREFIXES(AlsaPcmOutputStreamTest, AutoSelectDevice_HintFail); |
| 104 FRIEND_TEST_ALL_PREFIXES(AlsaPcmOutputStreamTest, BufferPacket); | 95 FRIEND_TEST_ALL_PREFIXES(AlsaPcmOutputStreamTest, BufferPacket); |
| 105 FRIEND_TEST_ALL_PREFIXES(AlsaPcmOutputStreamTest, BufferPacket_Negative); | 96 FRIEND_TEST_ALL_PREFIXES(AlsaPcmOutputStreamTest, BufferPacket_Negative); |
| 106 FRIEND_TEST_ALL_PREFIXES(AlsaPcmOutputStreamTest, BufferPacket_StopStream); | 97 FRIEND_TEST_ALL_PREFIXES(AlsaPcmOutputStreamTest, BufferPacket_StopStream); |
| 107 FRIEND_TEST_ALL_PREFIXES(AlsaPcmOutputStreamTest, BufferPacket_Underrun); | 98 FRIEND_TEST_ALL_PREFIXES(AlsaPcmOutputStreamTest, BufferPacket_Underrun); |
| 108 FRIEND_TEST_ALL_PREFIXES(AlsaPcmOutputStreamTest, BufferPacket_FullBuffer); | 99 FRIEND_TEST_ALL_PREFIXES(AlsaPcmOutputStreamTest, BufferPacket_FullBuffer); |
| 109 FRIEND_TEST_ALL_PREFIXES(AlsaPcmOutputStreamTest, ConstructedState); | 100 FRIEND_TEST_ALL_PREFIXES(AlsaPcmOutputStreamTest, ConstructedState); |
| 110 FRIEND_TEST_ALL_PREFIXES(AlsaPcmOutputStreamTest, LatencyFloor); | 101 FRIEND_TEST_ALL_PREFIXES(AlsaPcmOutputStreamTest, LatencyFloor); |
| 111 FRIEND_TEST_ALL_PREFIXES(AlsaPcmOutputStreamTest, OpenClose); | 102 FRIEND_TEST_ALL_PREFIXES(AlsaPcmOutputStreamTest, OpenClose); |
| 112 FRIEND_TEST_ALL_PREFIXES(AlsaPcmOutputStreamTest, PcmOpenFailed); | 103 FRIEND_TEST_ALL_PREFIXES(AlsaPcmOutputStreamTest, PcmOpenFailed); |
| 113 FRIEND_TEST_ALL_PREFIXES(AlsaPcmOutputStreamTest, PcmSetParamsFailed); | 104 FRIEND_TEST_ALL_PREFIXES(AlsaPcmOutputStreamTest, PcmSetParamsFailed); |
| 114 FRIEND_TEST_ALL_PREFIXES(AlsaPcmOutputStreamTest, ScheduleNextWrite); | 105 FRIEND_TEST_ALL_PREFIXES(AlsaPcmOutputStreamTest, ScheduleNextWrite); |
| 115 FRIEND_TEST_ALL_PREFIXES(AlsaPcmOutputStreamTest, | 106 FRIEND_TEST_ALL_PREFIXES(AlsaPcmOutputStreamTest, |
| 116 ScheduleNextWrite_StopStream); | 107 ScheduleNextWrite_StopStream); |
| 117 FRIEND_TEST_ALL_PREFIXES(AlsaPcmOutputStreamTest, StartStop); | 108 FRIEND_TEST_ALL_PREFIXES(AlsaPcmOutputStreamTest, StartStop); |
| 118 FRIEND_TEST_ALL_PREFIXES(AlsaPcmOutputStreamTest, WritePacket_FinishedPacket); | 109 FRIEND_TEST_ALL_PREFIXES(AlsaPcmOutputStreamTest, WritePacket_FinishedPacket); |
| 119 FRIEND_TEST_ALL_PREFIXES(AlsaPcmOutputStreamTest, WritePacket_NormalPacket); | 110 FRIEND_TEST_ALL_PREFIXES(AlsaPcmOutputStreamTest, WritePacket_NormalPacket); |
| 120 FRIEND_TEST_ALL_PREFIXES(AlsaPcmOutputStreamTest, WritePacket_StopStream); | 111 FRIEND_TEST_ALL_PREFIXES(AlsaPcmOutputStreamTest, WritePacket_StopStream); |
| 121 FRIEND_TEST_ALL_PREFIXES(AlsaPcmOutputStreamTest, WritePacket_WriteFails); | 112 FRIEND_TEST_ALL_PREFIXES(AlsaPcmOutputStreamTest, WritePacket_WriteFails); |
| 122 | 113 |
| 123 virtual ~AlsaPcmOutputStream(); | |
| 124 | |
| 125 // Flags indicating the state of the stream. | 114 // Flags indicating the state of the stream. |
| 126 enum InternalState { | 115 enum InternalState { |
| 127 kInError = 0, | 116 kInError = 0, |
| 128 kCreated, | 117 kCreated, |
| 129 kIsOpened, | 118 kIsOpened, |
| 130 kIsPlaying, | 119 kIsPlaying, |
| 131 kIsStopped, | 120 kIsStopped, |
| 132 kIsClosed | 121 kIsClosed |
| 133 }; | 122 }; |
| 134 friend std::ostream& operator<<(std::ostream& os, InternalState); | 123 friend std::ostream& operator<<(std::ostream& os, InternalState); |
| (...skipping 14 matching lines...) Expand all Loading... |
| 149 static uint32 FramesToMicros(uint32 frames, uint32 sample_rate); | 138 static uint32 FramesToMicros(uint32 frames, uint32 sample_rate); |
| 150 static uint32 FramesToMillis(uint32 frames, uint32 sample_rate); | 139 static uint32 FramesToMillis(uint32 frames, uint32 sample_rate); |
| 151 std::string FindDeviceForChannels(uint32 channels); | 140 std::string FindDeviceForChannels(uint32 channels); |
| 152 snd_pcm_sframes_t GetAvailableFrames(); | 141 snd_pcm_sframes_t GetAvailableFrames(); |
| 153 snd_pcm_sframes_t GetCurrentDelay(); | 142 snd_pcm_sframes_t GetCurrentDelay(); |
| 154 | 143 |
| 155 // Attempts to find the best matching linux audio device for the given number | 144 // Attempts to find the best matching linux audio device for the given number |
| 156 // of channels. This function will set |device_name_| and |should_downmix_|. | 145 // of channels. This function will set |device_name_| and |should_downmix_|. |
| 157 snd_pcm_t* AutoSelectDevice(uint32 latency); | 146 snd_pcm_t* AutoSelectDevice(uint32 latency); |
| 158 | 147 |
| 159 // Thread-asserting accessors for member variables. | 148 // Functions to safeguard state transitions. All changes to the object state |
| 160 AudioManagerLinux* manager(); | 149 // should go through these functions. |
| 150 bool CanTransitionTo(InternalState to); |
| 151 InternalState TransitionTo(InternalState to); |
| 152 InternalState state(); |
| 161 | 153 |
| 162 // Struct holding all mutable the data that must be shared by the | 154 // API for Proxying calls to the AudioSourceCallback provided during |
| 163 // message_loop() and the thread that created the object. | 155 // Start(). |
| 164 class SharedData { | 156 // |
| 165 public: | 157 // TODO(ajwong): This is necessary because the ownership semantics for the |
| 166 explicit SharedData(MessageLoop* state_transition_loop); | 158 // |source_callback_| object are incorrect in AudioRenderHost. The callback |
| 159 // is passed into the output stream, but ownership is not transfered which |
| 160 // requires a synchronization on access of the |source_callback_| to avoid |
| 161 // using a deleted callback. |
| 162 uint32 RunDataCallback(uint8* dest, |
| 163 uint32 max_size, |
| 164 AudioBuffersState buffers_state); |
| 165 void RunErrorCallback(int code); |
| 167 | 166 |
| 168 // Functions to safeguard state transitions and ensure that transitions are | 167 // Changes the AudioSourceCallback to proxy calls to. Pass in NULL to |
| 169 // only allowed occuring on the thread that created the object. All | 168 // release ownership of the currently registered callback. |
| 170 // changes to the object state should go through these functions. | 169 void set_source_callback(AudioSourceCallback* callback); |
| 171 bool CanTransitionTo(InternalState to); | |
| 172 bool CanTransitionTo_Locked(InternalState to); | |
| 173 InternalState TransitionTo(InternalState to); | |
| 174 InternalState state(); | |
| 175 | |
| 176 float volume(); | |
| 177 void set_volume(float v); | |
| 178 | |
| 179 // API for Proxying calls to the AudioSourceCallback provided during | |
| 180 // Start(). These APIs are threadsafe. | |
| 181 // | |
| 182 // TODO(ajwong): This is necessary because the ownership semantics for the | |
| 183 // |source_callback_| object are incorrect in AudioRenderHost. The callback | |
| 184 // is passed into the output stream, but ownership is not transfered which | |
| 185 // requires a synchronization on access of the |source_callback_| to avoid | |
| 186 // using a deleted callback. | |
| 187 uint32 OnMoreData(AudioOutputStream* stream, uint8* dest, | |
| 188 uint32 max_size, AudioBuffersState buffers_state); | |
| 189 void OnError(AudioOutputStream* stream, int code); | |
| 190 | |
| 191 // Changes the AudioSourceCallback to proxy calls to. Pass in NULL to | |
| 192 // release ownership of the currently registered callback. | |
| 193 void set_source_callback(AudioSourceCallback* callback); | |
| 194 | |
| 195 private: | |
| 196 base::Lock lock_; | |
| 197 | |
| 198 InternalState state_; | |
| 199 float volume_; // Volume level from 0.0 to 1.0. | |
| 200 | |
| 201 AudioSourceCallback* source_callback_; | |
| 202 | |
| 203 MessageLoop* const state_transition_loop_; | |
| 204 | |
| 205 DISALLOW_COPY_AND_ASSIGN(SharedData); | |
| 206 } shared_data_; | |
| 207 | 170 |
| 208 // Configuration constants from the constructor. Referenceable by all threads | 171 // Configuration constants from the constructor. Referenceable by all threads |
| 209 // since they are constants. | 172 // since they are constants. |
| 210 const std::string requested_device_name_; | 173 const std::string requested_device_name_; |
| 211 const snd_pcm_format_t pcm_format_; | 174 const snd_pcm_format_t pcm_format_; |
| 212 const uint32 channels_; | 175 const uint32 channels_; |
| 213 const uint32 sample_rate_; | 176 const uint32 sample_rate_; |
| 214 const uint32 bytes_per_sample_; | 177 const uint32 bytes_per_sample_; |
| 215 const uint32 bytes_per_frame_; | 178 const uint32 bytes_per_frame_; |
| 216 | 179 |
| 217 // Device configuration data. Populated after OpenTask() completes. | 180 // Device configuration data. Populated after OpenTask() completes. |
| 218 std::string device_name_; | 181 std::string device_name_; |
| 219 bool should_downmix_; | 182 bool should_downmix_; |
| 220 uint32 packet_size_; | 183 uint32 packet_size_; |
| 221 uint32 micros_per_packet_; | 184 uint32 micros_per_packet_; |
| 222 uint32 latency_micros_; | 185 uint32 latency_micros_; |
| 223 uint32 bytes_per_output_frame_; | 186 uint32 bytes_per_output_frame_; |
| 224 uint32 alsa_buffer_frames_; | 187 uint32 alsa_buffer_frames_; |
| 225 | 188 |
| 226 // Flag indicating the code should stop reading from the data source or | 189 // Flag indicating the code should stop reading from the data source or |
| 227 // writing to the ALSA device. This is set because the device has entered | 190 // writing to the ALSA device. This is set because the device has entered |
| 228 // an unrecoverable error state, or the ClosedTask() has executed. | 191 // an unrecoverable error state, or the ClosedTask() has executed. |
| 229 bool stop_stream_; | 192 bool stop_stream_; |
| 230 | 193 |
| 231 // Wrapper class to invoke all the ALSA functions. | 194 // Wrapper class to invoke all the ALSA functions. |
| 232 AlsaWrapper* wrapper_; | 195 AlsaWrapper* wrapper_; |
| 233 | 196 |
| 234 // Audio manager that created us. Used to report that we've been closed. | 197 // Audio manager that created us. Used to report that we've been closed. |
| 235 // This should only be used on the |client_thread_loop_|. Access via | |
| 236 // the manager() function. | |
| 237 AudioManagerLinux* manager_; | 198 AudioManagerLinux* manager_; |
| 238 | 199 |
| 239 // Handle to the actual PCM playback device. | 200 // Handle to the actual PCM playback device. |
| 240 snd_pcm_t* playback_handle_; | 201 snd_pcm_t* playback_handle_; |
| 241 | 202 |
| 242 scoped_ptr<media::SeekableBuffer> buffer_; | 203 scoped_ptr<media::SeekableBuffer> buffer_; |
| 243 uint32 frames_per_packet_; | 204 uint32 frames_per_packet_; |
| 244 | 205 |
| 245 // Used to check which message loop is allowed to call the public APIs. | |
| 246 MessageLoop* client_thread_loop_; | |
| 247 | |
| 248 // The message loop responsible for querying the data source, and writing to | 206 // The message loop responsible for querying the data source, and writing to |
| 249 // the output device. | 207 // the output device. |
| 250 MessageLoop* message_loop_; | 208 MessageLoop* message_loop_; |
| 251 | 209 |
| 210 // Allows us to run tasks on the AlsaPcmOutputStream instance which are |
| 211 // bound by its lifetime. |
| 212 ScopedRunnableMethodFactory<AlsaPcmOutputStream> method_factory_; |
| 213 |
| 214 InternalState state_; |
| 215 float volume_; // Volume level from 0.0 to 1.0. |
| 216 |
| 217 AudioSourceCallback* source_callback_; |
| 218 |
| 252 DISALLOW_COPY_AND_ASSIGN(AlsaPcmOutputStream); | 219 DISALLOW_COPY_AND_ASSIGN(AlsaPcmOutputStream); |
| 253 }; | 220 }; |
| 254 | 221 |
| 255 #endif // MEDIA_AUDIO_LINUX_ALSA_OUTPUT_H_ | 222 #endif // MEDIA_AUDIO_LINUX_ALSA_OUTPUT_H_ |
| OLD | NEW |