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
232
233
234
235
media / audio / alsa / alsa_output.h [blame]
// Copyright 2013 The Chromium Authors
// 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.
//
// If the stream is successfully opened, Close() must be called. After Close
// has been called, the object should be regarded as deleted and not touched.
//
// AlsaPcmOutputStream is a single threaded class that should only be used from
// the audio thread. When modifying the code in this class, please read the
// threading assumptions at the top of the implementation.
#ifndef MEDIA_AUDIO_ALSA_ALSA_OUTPUT_H_
#define MEDIA_AUDIO_ALSA_ALSA_OUTPUT_H_
#include <alsa/asoundlib.h>
#include <stdint.h>
#include <memory>
#include <string>
#include "base/compiler_specific.h"
#include "base/gtest_prod_util.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/scoped_refptr.h"
#include "base/memory/weak_ptr.h"
#include "base/sequence_checker.h"
#include "base/task/single_thread_task_runner.h"
#include "base/time/tick_clock.h"
#include "base/time/time.h"
#include "media/audio/audio_io.h"
#include "media/base/audio_parameters.h"
namespace media {
class AlsaWrapper;
class AudioManagerBase;
class ChannelMixer;
class SeekableBuffer;
class MEDIA_EXPORT AlsaPcmOutputStream : public AudioOutputStream {
public:
// String for the generic "default" ALSA device that has the highest
// compatibility and chance of working.
static constexpr char kDefaultDevice[] = "default";
// Pass this to the AlsaPcmOutputStream if you want to attempt auto-selection
// of the audio device.
static constexpr char kAutoSelectDevice[] = "";
// Prefix for device names to enable ALSA library resampling.
static constexpr char kPlugPrefix[] = "plug:";
// The minimum latency that is accepted by the device.
// We use 40ms as our minimum required latency. If it is needed, we may be
// able to get it down to 20ms.
static constexpr uint32_t kMinLatencyMicros = 40 * 1000;
// 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
// the current thread.
//
// If unsure of what to use for |device_name|, use |kAutoSelectDevice|.
AlsaPcmOutputStream(const std::string& device_name,
const AudioParameters& params,
AlsaWrapper* wrapper,
AudioManagerBase* manager);
AlsaPcmOutputStream(const AlsaPcmOutputStream&) = delete;
AlsaPcmOutputStream& operator=(const AlsaPcmOutputStream&) = delete;
~AlsaPcmOutputStream() override;
// Implementation of AudioOutputStream.
bool Open() override;
void Close() override;
void Start(AudioSourceCallback* callback) override;
void Stop() override;
void Flush() override;
void SetVolume(double volume) override;
void GetVolume(double* volume) override;
void SetTickClockForTesting(const base::TickClock* tick_clock);
private:
friend class AlsaPcmOutputStreamTest;
FRIEND_TEST_ALL_PREFIXES(AlsaPcmOutputStreamTest,
AutoSelectDevice_DeviceSelect);
FRIEND_TEST_ALL_PREFIXES(AlsaPcmOutputStreamTest,
AutoSelectDevice_FallbackDevices);
FRIEND_TEST_ALL_PREFIXES(AlsaPcmOutputStreamTest, AutoSelectDevice_HintFail);
FRIEND_TEST_ALL_PREFIXES(AlsaPcmOutputStreamTest, BufferPacket);
FRIEND_TEST_ALL_PREFIXES(AlsaPcmOutputStreamTest, BufferPacket_Negative);
FRIEND_TEST_ALL_PREFIXES(AlsaPcmOutputStreamTest, BufferPacket_StopStream);
FRIEND_TEST_ALL_PREFIXES(AlsaPcmOutputStreamTest, BufferPacket_Underrun);
FRIEND_TEST_ALL_PREFIXES(AlsaPcmOutputStreamTest, BufferPacket_FullBuffer);
FRIEND_TEST_ALL_PREFIXES(AlsaPcmOutputStreamTest, ConstructedState);
FRIEND_TEST_ALL_PREFIXES(AlsaPcmOutputStreamTest, LatencyFloor);
FRIEND_TEST_ALL_PREFIXES(AlsaPcmOutputStreamTest, OpenClose);
FRIEND_TEST_ALL_PREFIXES(AlsaPcmOutputStreamTest, PcmOpenFailed);
FRIEND_TEST_ALL_PREFIXES(AlsaPcmOutputStreamTest, PcmSetParamsFailed);
FRIEND_TEST_ALL_PREFIXES(AlsaPcmOutputStreamTest, ScheduleNextWrite);
FRIEND_TEST_ALL_PREFIXES(AlsaPcmOutputStreamTest,
ScheduleNextWrite_StopStream);
FRIEND_TEST_ALL_PREFIXES(AlsaPcmOutputStreamTest, StartStop);
FRIEND_TEST_ALL_PREFIXES(AlsaPcmOutputStreamTest, WritePacket_FinishedPacket);
FRIEND_TEST_ALL_PREFIXES(AlsaPcmOutputStreamTest, WritePacket_NormalPacket);
FRIEND_TEST_ALL_PREFIXES(AlsaPcmOutputStreamTest, WritePacket_StopStream);
FRIEND_TEST_ALL_PREFIXES(AlsaPcmOutputStreamTest, WritePacket_WriteFails);
// Flags indicating the state of the stream.
enum InternalState {
kInError = 0,
kCreated,
kIsOpened,
kIsPlaying,
kIsStopped,
kIsClosed
};
friend std::ostream& operator<<(std::ostream& os, InternalState);
// Functions to get another packet from the data source and write it into the
// ALSA device.
void BufferPacket(bool* source_exhausted);
void WritePacket();
void WriteTask();
void ScheduleNextWrite(bool source_exhausted);
// Utility functions for talking with the ALSA API.
std::string FindDeviceForChannels(uint32_t channels);
snd_pcm_sframes_t GetAvailableFrames();
snd_pcm_sframes_t GetCurrentDelay();
// Attempts to find the best matching linux audio device for the given number
// of channels. This function will set |device_name_| and |channel_mixer_|.
snd_pcm_t* AutoSelectDevice(uint32_t latency);
// Functions to safeguard state transitions. All changes to the object state
// should go through these functions.
bool CanTransitionTo(InternalState to);
InternalState TransitionTo(InternalState to);
InternalState state();
// API for Proxying calls to the AudioSourceCallback provided during
// Start().
//
// 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 transferred which
// requires a synchronization on access of the |source_callback_| to avoid
// using a deleted callback.
int RunDataCallback(base::TimeDelta delay,
base::TimeTicks delay_timestamp,
AudioBus* audio_bus);
void RunErrorCallback(int code);
// Changes the AudioSourceCallback to proxy calls to. Pass in nullptr to
// release ownership of the currently registered callback.
void set_source_callback(AudioSourceCallback* callback);
// Configuration constants from the constructor. Referenceable by all threads
// since they are constants.
const std::string requested_device_name_;
const snd_pcm_format_t pcm_format_;
const uint32_t channels_;
const ChannelLayout channel_layout_;
const uint32_t sample_rate_;
const uint32_t bytes_per_sample_;
const uint32_t bytes_per_frame_;
// Device configuration data. Populated after OpenTask() completes.
std::string device_name_;
uint32_t packet_size_;
base::TimeDelta latency_;
uint32_t bytes_per_output_frame_;
uint32_t alsa_buffer_frames_;
// 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.
raw_ptr<AlsaWrapper> wrapper_;
// Audio manager that created us. Used to report that we've been closed.
raw_ptr<AudioManagerBase> manager_;
// Task runner to use for polling.
const scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
// Handle to the actual PCM playback device.
raw_ptr<snd_pcm_t> playback_handle_;
std::unique_ptr<SeekableBuffer> buffer_;
uint32_t frames_per_packet_;
InternalState state_;
float volume_; // Volume level from 0.0 to 1.0.
raw_ptr<AudioSourceCallback> source_callback_;
// Container for retrieving data from AudioSourceCallback::OnMoreData().
std::unique_ptr<AudioBus> audio_bus_;
// Channel mixer and temporary bus for the final mixed channel data.
std::unique_ptr<ChannelMixer> channel_mixer_;
std::unique_ptr<AudioBus> mixed_audio_bus_;
raw_ptr<const base::TickClock> tick_clock_;
SEQUENCE_CHECKER(sequence_checker_);
// Allows us to run tasks on the AlsaPcmOutputStream instance which are
// bound by its lifetime.
// NOTE: Weak pointers must be invalidated before all other member variables.
base::WeakPtrFactory<AlsaPcmOutputStream> weak_factory_{this};
};
MEDIA_EXPORT std::ostream& operator<<(std::ostream& os,
AlsaPcmOutputStream::InternalState);
} // namespace media
#endif // MEDIA_AUDIO_ALSA_ALSA_OUTPUT_H_