Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(84)

Side by Side Diff: media/audio/linux/alsa_output.h

Issue 160497: Reimplement the AlsaPcmOutputStream and fix the threading issues. (Closed)
Patch Set: Address Andrew's comments. Diff aginst Patch 8 please. Created 11 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « media/audio/audio_output.h ('k') | media/audio/linux/alsa_output.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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_
OLDNEW
« no previous file with comments | « media/audio/audio_output.h ('k') | media/audio/linux/alsa_output.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698