| OLD | NEW |
| 1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2009 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. The current | 5 // Creates an output stream based on the ALSA PCM interface. |
| 6 // implementation creates one thread per ALSA playback handle that is | |
| 7 // responsible for synchronously pulling data from the audio data source. | |
| 8 // | 6 // |
| 9 // This output stream buffers in two places: | 7 // On device write failure, the stream will move itself to an invalid state. |
| 10 // (1) In the ALSA device | 8 // No more data will be pulled from the data source, or written to the device. |
| 11 // (2) In an in-memory buffer. | 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() |
| 11 // will return false, and Start() will call OnError() immediately on the |
| 12 // provided callback. |
| 12 // | 13 // |
| 13 // The ALSA device buffer is kept as full as possible. The in-memory buffer | 14 // TODO(ajwong): The OnClose() and OnError() calling needing fixing. |
| 14 // attempts to keep enough extra data so that |min_buffer_ms| worth of data | |
| 15 // is available between the in-memory buffer and the device buffer. Requests | |
| 16 // to the audio data source are made if the total amount buffered falls below | |
| 17 // |min_buffer_ms|. | |
| 18 // | |
| 19 // On device write failure, the stream will move into an invalid state. No | |
| 20 // more data will be pulled from the data source, and the playback thread will | |
| 21 // be stopped. | |
| 22 // | 15 // |
| 23 // 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 |
| 24 // stream is deleted. | 17 // stream is deleted as Close() is responsible for ensuring resource cleanup |
| 18 // occurs. |
| 19 // |
| 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 |
| 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 |
| 24 // threading assumptions at the top of the implementation file to avoid |
| 25 // introducing race conditions between tasks posted to the internal |
| 26 // message_loop, and the thread calling the public APIs. |
| 25 | 27 |
| 26 #ifndef MEDIA_AUDIO_LINUX_ALSA_OUTPUT_H_ | 28 #ifndef MEDIA_AUDIO_LINUX_ALSA_OUTPUT_H_ |
| 27 #define MEDIA_AUDIO_LINUX_ALSA_OUTPUT_H_ | 29 #define MEDIA_AUDIO_LINUX_ALSA_OUTPUT_H_ |
| 28 | 30 |
| 29 #include <alsa/asoundlib.h> | 31 #include <alsa/asoundlib.h> |
| 30 | 32 |
| 31 #include <deque> | |
| 32 #include <string> | 33 #include <string> |
| 33 | 34 |
| 34 #include "base/lock.h" | 35 #include "base/lock.h" |
| 35 #include "base/ref_counted.h" | 36 #include "base/ref_counted.h" |
| 36 #include "base/scoped_ptr.h" | 37 #include "base/scoped_ptr.h" |
| 37 #include "base/thread.h" | 38 #include "base/thread.h" |
| 38 #include "media/audio/audio_output.h" | 39 #include "media/audio/audio_output.h" |
| 40 #include "testing/gtest/include/gtest/gtest_prod.h" |
| 39 | 41 |
| 40 class Thread; | 42 class AlsaWrapper; |
| 41 | 43 |
| 42 class AlsaPCMOutputStream : | 44 class AlsaPcmOutputStream : |
| 43 public AudioOutputStream, | 45 public AudioOutputStream, |
| 44 public base::RefCountedThreadSafe<AlsaPCMOutputStream> { | 46 public base::RefCountedThreadSafe<AlsaPcmOutputStream> { |
| 45 public: | 47 public: |
| 46 // Set to "default" which should avoid locking the sound device and allow | 48 // Set to "default" which should avoid locking the sound device and allow |
| 47 // ALSA to multiplex sound from different processes that want to write PCM | 49 // ALSA to multiplex sound from different processes that want to write PCM |
| 48 // data. | 50 // data. |
| 49 static const char* kDefaultDevice; | 51 static const char kDefaultDevice[]; |
| 50 | 52 |
| 51 // Create a PCM Output stream for the ALSA device identified by | 53 // Create a PCM Output stream for the ALSA device identified by |
| 52 // |device_name|. If unsure of hte device_name, use kDefaultDevice. | 54 // |device_name|. The AlsaPcmOutputStream uses |wrapper| to communicate with |
| 53 AlsaPCMOutputStream(const std::string& device_name, | 55 // the alsa libraries, allowing for dependency injection during testing. All |
| 54 int min_buffer_ms, | 56 // requesting of data, and writing to the alsa device will be done on |
| 57 // |message_loop|. |
| 58 // |
| 59 // If unsure of what to use for |device_name|, use |kDefaultDevice|. |
| 60 AlsaPcmOutputStream(const std::string& device_name, |
| 55 AudioManager::Format format, | 61 AudioManager::Format format, |
| 56 int channels, | 62 int channels, |
| 57 int sample_rate, | 63 int sample_rate, |
| 58 char bits_per_sample); | 64 int bits_per_sample, |
| 59 virtual ~AlsaPCMOutputStream(); | 65 AlsaWrapper* wrapper, |
| 66 MessageLoop* message_loop); |
| 67 virtual ~AlsaPcmOutputStream(); |
| 60 | 68 |
| 61 // Implementation of AudioOutputStream. | 69 // Implementation of AudioOutputStream. |
| 62 virtual bool Open(size_t packet_size); | 70 virtual bool Open(size_t packet_size); |
| 63 virtual void Close(); | 71 virtual void Close(); |
| 64 virtual void Start(AudioSourceCallback* callback); | 72 virtual void Start(AudioSourceCallback* callback); |
| 65 virtual void Stop(); | 73 virtual void Stop(); |
| 66 virtual void SetVolume(double left_level, double right_level); | 74 virtual void SetVolume(double left_level, double right_level); |
| 67 virtual void GetVolume(double* left_level, double* right_level); | 75 virtual void GetVolume(double* left_level, double* right_level); |
| 68 | 76 |
| 69 private: | 77 private: |
| 70 // Closes the playback handle, reporting errors if any occur. Returns true | 78 friend class AlsaPcmOutputStreamTest; |
| 71 // if the device was successfully closed. | 79 FRIEND_TEST(AlsaPcmOutputStreamTest, BufferPacket); |
| 72 bool CloseDevice_Locked(); | 80 FRIEND_TEST(AlsaPcmOutputStreamTest, BufferPacket_StopStream); |
| 73 | 81 FRIEND_TEST(AlsaPcmOutputStreamTest, BufferPacket_UnfinishedPacket); |
| 74 // Stops playback, ignoring state checks. | 82 FRIEND_TEST(AlsaPcmOutputStreamTest, ConstructedState); |
| 75 void StopInternal_Locked(); | 83 FRIEND_TEST(AlsaPcmOutputStreamTest, OpenClose); |
| 76 | 84 FRIEND_TEST(AlsaPcmOutputStreamTest, PcmOpenFailed); |
| 77 // Moves the stream into the error state, setting the correct internal flags. | 85 FRIEND_TEST(AlsaPcmOutputStreamTest, PcmSetParamsFailed); |
| 78 // Ensure that all resources are cleaned up before executing this function. | 86 FRIEND_TEST(AlsaPcmOutputStreamTest, ScheduleNextWrite); |
| 79 void EnterStateError_Locked(); | 87 FRIEND_TEST(AlsaPcmOutputStreamTest, ScheduleNextWrite_StopStream); |
| 80 | 88 FRIEND_TEST(AlsaPcmOutputStreamTest, StartStop); |
| 81 // Releases all the resources in the audio stream. This method will also | 89 FRIEND_TEST(AlsaPcmOutputStreamTest, WritePacket_FinishedPacket); |
| 82 // terminate the playback thread itself. | 90 FRIEND_TEST(AlsaPcmOutputStreamTest, WritePacket_NormalPacket); |
| 83 // | 91 FRIEND_TEST(AlsaPcmOutputStreamTest, WritePacket_StopStream); |
| 84 // This method must be run in the |playback_thead_|. | 92 FRIEND_TEST(AlsaPcmOutputStreamTest, WritePacket_WriteFails); |
| 85 void ReleaseResources(); | |
| 86 | |
| 87 // Retrieve the total number of frames buffered in both memory and in the | |
| 88 // audio device. Use this to determine if more data should be requested from | |
| 89 // the audio source. | |
| 90 snd_pcm_sframes_t GetFramesOfDelay_Locked(); | |
| 91 | |
| 92 // Buffer more packets from data source if necessary. | |
| 93 // | |
| 94 // This function must be run in the |playback_thread_|. | |
| 95 void BufferPackets(); | |
| 96 | |
| 97 // Returns true if our buffer is underfull. | |
| 98 bool ShouldBufferMore_NoLock(); | |
| 99 | |
| 100 // Write as many buffered packets into the device as there is room in the | |
| 101 // device buffer. | |
| 102 // | |
| 103 // This function must be run in the |playback_thread_|. | |
| 104 void FillAlsaDeviceBuffer(); | |
| 105 | |
| 106 // State of the stream. | |
| 107 State state_; | |
| 108 | |
| 109 // The ALSA device name to use. | |
| 110 std::string device_name_; | |
| 111 | |
| 112 // Handle to the actual PCM playback device. | |
| 113 snd_pcm_t* playback_handle_; | |
| 114 | |
| 115 // Period size for ALSA ring-buffer. Basically, how long to wait between | |
| 116 // writes. | |
| 117 snd_pcm_sframes_t period_size_; | |
| 118 | |
| 119 // Callback used to request more data from the data source. | |
| 120 AudioSourceCallback* source_callback_; | |
| 121 | |
| 122 // Playback thread. | |
| 123 base::Thread playback_thread_; | |
| 124 | |
| 125 // Lock for field access to this object. | |
| 126 Lock lock_; | |
| 127 | |
| 128 // Sample format configuration. | |
| 129 snd_pcm_format_t pcm_format_; | |
| 130 const int channels_; | |
| 131 const int sample_rate_; | |
| 132 const char bits_per_sample_; | |
| 133 char bytes_per_frame_; | |
| 134 | 93 |
| 135 // In-memory buffer to hold sound samples before pushing to the sound device. | 94 // In-memory buffer to hold sound samples before pushing to the sound device. |
| 136 // TODO(ajwong): There are now about 3 buffer queue implementations. Factor | 95 // TODO(ajwong): There are now about 3 buffer/packet implementations. Factor |
| 137 // them out. | 96 // them out. |
| 138 struct Packet { | 97 struct Packet { |
| 139 explicit Packet(int new_capacity) | 98 explicit Packet(int new_capacity) |
| 140 : capacity(new_capacity), | 99 : capacity(new_capacity), |
| 141 size(0), | 100 size(0), |
| 142 used(0), | 101 used(0), |
| 143 buffer(new char[capacity]) { | 102 buffer(new char[capacity]) { |
| 144 } | 103 } |
| 145 size_t capacity; | 104 size_t capacity; |
| 146 size_t size; | 105 size_t size; |
| 147 size_t used; | 106 size_t used; |
| 148 scoped_array<char> buffer; | 107 scoped_array<char> buffer; |
| 149 }; | 108 }; |
| 150 int min_buffer_frames_; | |
| 151 std::deque<Packet*> buffered_packets_; | |
| 152 size_t packet_size_; | |
| 153 | 109 |
| 154 // Flag indiciating the device write tasks have stopped scheduling | 110 // Flags indicating the state of the stream. |
| 155 // themselves. This should only be modified by the BufferPackets() and | 111 enum InternalState { |
| 156 // FillAlsaDeviceBuffer() methods. | 112 kInError = 0, |
| 157 bool device_write_suspended_; | 113 kCreated, |
| 114 kIsOpened, |
| 115 kIsPlaying, |
| 116 kIsStopped, |
| 117 kIsClosed |
| 118 }; |
| 119 friend std::ostream& ::operator<<(std::ostream& os, InternalState); |
| 158 | 120 |
| 159 // Flag indicating that the resources are already cleaned. | 121 // Various tasks that complete actions started in the public API. |
| 160 bool resources_released_; | 122 void FinishOpen(snd_pcm_t* playback_handle, size_t packet_size); |
| 123 void StartTask(AudioSourceCallback* callback); |
| 124 void CloseTask(); |
| 161 | 125 |
| 162 // Volume level from 0 to 1. | 126 // Functions to get another packet from the data source and write it into the |
| 163 float volume_; | 127 // ALSA device. |
| 128 void BufferPacket(Packet* packet); |
| 129 void WritePacket(Packet* packet); |
| 130 void WriteTask(); |
| 131 void ScheduleNextWrite(Packet* current_packet); |
| 164 | 132 |
| 165 DISALLOW_COPY_AND_ASSIGN(AlsaPCMOutputStream); | 133 // Functions to safeguard state transitions and ensure that transitions are |
| 134 // only allowed occuring on the thread that created the object. All changes |
| 135 // to the object state should go through these functions. |
| 136 bool CanTransitionTo(InternalState to); |
| 137 bool CanTransitionTo_Locked(InternalState to); |
| 138 InternalState TransitionTo(InternalState to); |
| 139 |
| 140 // Utility functions for talking with the ALSA API. |
| 141 static snd_pcm_sframes_t FramesInPacket(const Packet& packet, |
| 142 int bytes_per_frame); |
| 143 static int64 FramesToMicros(int frames, int sample_rate); |
| 144 static int64 FramesToMillis(int frames, int sample_rate); |
| 145 bool CloseDevice(snd_pcm_t* handle); |
| 146 snd_pcm_sframes_t GetAvailableFrames(); |
| 147 |
| 148 // Struct holding all mutable the data that must be shared by the |
| 149 // message_loop() and the thread that created the object. |
| 150 class SharedData { |
| 151 public: |
| 152 explicit SharedData(MessageLoop* state_transition_loop); |
| 153 |
| 154 // Functions to safeguard state transitions and ensure that transitions are |
| 155 // only allowed occuring on the thread that created the object. All |
| 156 // changes to the object state should go through these functions. |
| 157 bool CanTransitionTo(InternalState to); |
| 158 bool CanTransitionTo_Locked(InternalState to); |
| 159 InternalState TransitionTo(InternalState to); |
| 160 InternalState state(); |
| 161 |
| 162 float volume(); |
| 163 void set_volume(float v); |
| 164 |
| 165 private: |
| 166 Lock lock_; |
| 167 |
| 168 InternalState state_; |
| 169 float volume_; // Volume level from 0.0 to 1.0. |
| 170 |
| 171 MessageLoop* const state_transition_loop_; |
| 172 } shared_data_; |
| 173 |
| 174 // Configuration constants from the constructor. Referenceable by all threads |
| 175 // since they are constants. |
| 176 const std::string device_name_; |
| 177 const snd_pcm_format_t pcm_format_; |
| 178 const int channels_; |
| 179 const int sample_rate_; |
| 180 const int bytes_per_sample_; |
| 181 const int bytes_per_frame_; |
| 182 |
| 183 // Flag indicating the code should stop reading from the data source or |
| 184 // writing to the ALSA device. This is set because the device has entered |
| 185 // an unrecoverable error state, or the ClosedTask() has executed. |
| 186 bool stop_stream_; |
| 187 |
| 188 // Wrapper class to invoke all the ALSA functions. |
| 189 AlsaWrapper* wrapper_; |
| 190 |
| 191 // Handle to the actual PCM playback device. |
| 192 snd_pcm_t* playback_handle_; |
| 193 |
| 194 // Callback used to request more data from the data source. |
| 195 AudioSourceCallback* source_callback_; |
| 196 |
| 197 scoped_ptr<Packet> packet_; |
| 198 int frames_per_packet_; |
| 199 |
| 200 // Used to check which message loop is allowed to call the public APIs. |
| 201 MessageLoop* client_thread_loop_; |
| 202 |
| 203 // The message loop responsible for querying the data source, and writing to |
| 204 // the output device. |
| 205 MessageLoop* message_loop_; |
| 206 |
| 207 DISALLOW_COPY_AND_ASSIGN(AlsaPcmOutputStream); |
| 166 }; | 208 }; |
| 167 | 209 |
| 168 #endif // MEDIA_AUDIO_LINUX_ALSA_OUTPUT_H_ | 210 #endif // MEDIA_AUDIO_LINUX_ALSA_OUTPUT_H_ |
| OLD | NEW |