1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
|
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Creates an output stream based on the ALSA PCM interface.
//
// On device write failure, the stream will move itself to an invalid state.
// No more data will be pulled from the data source, or written to the device.
// All calls to public API functions will either no-op themselves, or return an
// error if possible. Specifically, If the stream is in an error state, Open()
// will return false, and Start() will call OnError() immediately on the
// provided callback.
//
// TODO(ajwong): The OnClose() and OnError() calling needing fixing.
//
// If the stream is successfully opened, Close() must be called before the
// stream is deleted as Close() is responsible for ensuring resource cleanup
// occurs.
//
// This object's thread-safety is a little tricky. This object's public API
// can only be called from the thread that created the object. Calling the
// public APIs in any method that may cause concurrent execution will result in
// a race condition. When modifying the code in this class, please read the
// threading assumptions at the top of the implementation file to avoid
// introducing race conditions between tasks posted to the internal
// message_loop, and the thread calling the public APIs.
#ifndef MEDIA_AUDIO_LINUX_ALSA_OUTPUT_H_
#define MEDIA_AUDIO_LINUX_ALSA_OUTPUT_H_
#include <alsa/asoundlib.h>
#include <string>
#include "base/lock.h"
#include "base/ref_counted.h"
#include "base/scoped_ptr.h"
#include "base/thread.h"
#include "media/audio/audio_output.h"
#include "testing/gtest/include/gtest/gtest_prod.h"
class AlsaWrapper;
class AudioManagerLinux;
class AlsaPcmOutputStream :
public AudioOutputStream,
public base::RefCountedThreadSafe<AlsaPcmOutputStream> {
public:
// Set to "default" which should avoid locking the sound device and allow
// ALSA to multiplex sound from different processes that want to write PCM
// data.
static const char kDefaultDevice[];
// Create a PCM Output stream for the ALSA device identified by
// |device_name|. The AlsaPcmOutputStream uses |wrapper| to communicate with
// the alsa libraries, allowing for dependency injection during testing. All
// requesting of data, and writing to the alsa device will be done on
// |message_loop|.
//
// If unsure of what to use for |device_name|, use |kDefaultDevice|.
AlsaPcmOutputStream(const std::string& device_name,
AudioManager::Format format,
int channels,
int sample_rate,
int bits_per_sample,
AlsaWrapper* wrapper,
AudioManagerLinux* manager,
MessageLoop* message_loop);
virtual ~AlsaPcmOutputStream();
// Implementation of AudioOutputStream.
virtual bool Open(size_t packet_size);
virtual void Close();
virtual void Start(AudioSourceCallback* callback);
virtual void Stop();
virtual void SetVolume(double left_level, double right_level);
virtual void GetVolume(double* left_level, double* right_level);
private:
friend class AlsaPcmOutputStreamTest;
FRIEND_TEST(AlsaPcmOutputStreamTest, BufferPacket);
FRIEND_TEST(AlsaPcmOutputStreamTest, BufferPacket_StopStream);
FRIEND_TEST(AlsaPcmOutputStreamTest, BufferPacket_UnfinishedPacket);
FRIEND_TEST(AlsaPcmOutputStreamTest, ConstructedState);
FRIEND_TEST(AlsaPcmOutputStreamTest, OpenClose);
FRIEND_TEST(AlsaPcmOutputStreamTest, PcmOpenFailed);
FRIEND_TEST(AlsaPcmOutputStreamTest, PcmSetParamsFailed);
FRIEND_TEST(AlsaPcmOutputStreamTest, ScheduleNextWrite);
FRIEND_TEST(AlsaPcmOutputStreamTest, ScheduleNextWrite_StopStream);
FRIEND_TEST(AlsaPcmOutputStreamTest, StartStop);
FRIEND_TEST(AlsaPcmOutputStreamTest, WritePacket_FinishedPacket);
FRIEND_TEST(AlsaPcmOutputStreamTest, WritePacket_NormalPacket);
FRIEND_TEST(AlsaPcmOutputStreamTest, WritePacket_StopStream);
FRIEND_TEST(AlsaPcmOutputStreamTest, WritePacket_WriteFails);
// In-memory buffer to hold sound samples before pushing to the sound device.
// TODO(ajwong): There are now about 3 buffer/packet implementations. Factor
// them out.
struct Packet {
explicit Packet(int new_capacity)
: capacity(new_capacity),
size(0),
used(0),
buffer(new char[capacity]) {
}
size_t capacity;
size_t size;
size_t used;
scoped_array<char> buffer;
};
// Flags indicating the state of the stream.
enum InternalState {
kInError = 0,
kCreated,
kIsOpened,
kIsPlaying,
kIsStopped,
kIsClosed
};
friend std::ostream& ::operator<<(std::ostream& os, InternalState);
// Various tasks that complete actions started in the public API.
void FinishOpen(snd_pcm_t* playback_handle, size_t packet_size);
void StartTask();
void CloseTask();
// Functions to get another packet from the data source and write it into the
// ALSA device.
void BufferPacket(Packet* packet);
void WritePacket(Packet* packet);
void WriteTask();
void ScheduleNextWrite(Packet* current_packet);
// Utility functions for talking with the ALSA API.
static snd_pcm_sframes_t FramesInPacket(const Packet& packet,
int bytes_per_frame);
static int64 FramesToMicros(int frames, int sample_rate);
static int64 FramesToMillis(int frames, int sample_rate);
bool CloseDevice(snd_pcm_t* handle);
snd_pcm_sframes_t GetAvailableFrames();
// Thread-asserting accessors for member variables.
AudioManagerLinux* manager();
// Struct holding all mutable the data that must be shared by the
// message_loop() and the thread that created the object.
class SharedData {
public:
explicit SharedData(MessageLoop* state_transition_loop);
// Functions to safeguard state transitions and ensure that transitions are
// only allowed occuring on the thread that created the object. All
// changes to the object state should go through these functions.
bool CanTransitionTo(InternalState to);
bool CanTransitionTo_Locked(InternalState to);
InternalState TransitionTo(InternalState to);
InternalState state();
float volume();
void set_volume(float v);
// API for Proxying calls to the AudioSourceCallback provided during
// Start(). These APIs are threadsafe.
//
// TODO(ajwong): This is necessary because the ownership semantics for the
// |source_callback_| object are incorrect in AudioRenderHost. The callback
// is passed into the output stream, but ownership is not transfered which
// requires a synchronization on access of the |source_callback_| to avoid
// using a deleted callback.
size_t OnMoreData(AudioOutputStream* stream, void* dest,
size_t max_size, int pending_bytes);
void OnClose(AudioOutputStream* stream);
void OnError(AudioOutputStream* stream, int code);
// Changes the AudioSourceCallback to proxy calls to. Pass in NULL to
// release ownership of the currently registered callback.
void set_source_callback(AudioSourceCallback* callback);
private:
Lock lock_;
InternalState state_;
float volume_; // Volume level from 0.0 to 1.0.
AudioSourceCallback* source_callback_;
MessageLoop* const state_transition_loop_;
DISALLOW_COPY_AND_ASSIGN(SharedData);
} shared_data_;
// Configuration constants from the constructor. Referenceable by all threads
// since they are constants.
const std::string device_name_;
const snd_pcm_format_t pcm_format_;
const int channels_;
const int sample_rate_;
const int bytes_per_sample_;
const int bytes_per_frame_;
// Flag indicating the code should stop reading from the data source or
// writing to the ALSA device. This is set because the device has entered
// an unrecoverable error state, or the ClosedTask() has executed.
bool stop_stream_;
// Wrapper class to invoke all the ALSA functions.
AlsaWrapper* wrapper_;
// Audio manager that created us. Used to report that we've been closed.
// This should only be used on the |client_thread_loop_|. Access via
// the manager() function.
AudioManagerLinux* manager_;
// Handle to the actual PCM playback device.
snd_pcm_t* playback_handle_;
scoped_ptr<Packet> packet_;
int frames_per_packet_;
// Used to check which message loop is allowed to call the public APIs.
MessageLoop* client_thread_loop_;
// The message loop responsible for querying the data source, and writing to
// the output device.
MessageLoop* message_loop_;
DISALLOW_COPY_AND_ASSIGN(AlsaPcmOutputStream);
};
#endif // MEDIA_AUDIO_LINUX_ALSA_OUTPUT_H_
|