1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "media/audio/cras/cras_unified.h"
6
7 #include <cras_client.h>
8
9 #include "base/command_line.h"
10 #include "base/logging.h"
11 #include "media/audio/alsa/alsa_util.h"
12 #include "media/audio/cras/audio_manager_cras.h"
13
14 namespace media {
15
16 // Overview of operation:
17 // 1) An object of CrasUnifiedStream is created by the AudioManager
18 // factory: audio_man->MakeAudioStream().
19 // 2) Next some thread will call Open(), at that point a client is created and
20 // configured for the correct format and sample rate.
21 // 3) Then Start(source) is called and a stream is added to the CRAS client
22 // which will create its own thread that periodically calls the source for more
23 // data as buffers are being consumed.
24 // 4) When finished Stop() is called, which is handled by stopping the stream.
25 // 5) Finally Close() is called. It cleans up and notifies the audio manager,
26 // which likely will destroy this object.
27 //
28 // For output-only streams, a unified stream is created with 0 input channels.
29 //
30 // Simplified data flow for unified streams:
31 //
32 // +-------------+ +------------------+
33 // | CRAS Server | | Chrome Client |
34 // +------+------+ Add Stream +---------+--------+
35 // |<----------------------------------|
36 // | |
37 // | buffer_frames captured to shm |
38 // |---------------------------------->|
39 // | | UnifiedCallback()
40 // | | ReadWriteAudio()
41 // | |
42 // | buffer_frames written to shm |
43 // |<----------------------------------|
44 // | |
45 // ... Repeats for each block. ...
46 // | |
47 // | |
48 // | Remove stream |
49 // |<----------------------------------|
50 // | |
51 //
52 // Simplified data flow for output only streams:
53 //
54 // +-------------+ +------------------+
55 // | CRAS Server | | Chrome Client |
56 // +------+------+ Add Stream +---------+--------+
57 // |<----------------------------------|
58 // | |
59 // | Near out of samples, request more |
60 // |---------------------------------->|
61 // | | UnifiedCallback()
62 // | | WriteAudio()
63 // | |
64 // | buffer_frames written to shm |
65 // |<----------------------------------|
66 // | |
67 // ... Repeats for each block. ...
68 // | |
69 // | |
70 // | Remove stream |
71 // |<----------------------------------|
72 // | |
73 //
74 // For Unified streams the Chrome client is notified whenever buffer_frames have
75 // been captured. For Output streams the client is notified a few milliseconds
76 // before the hardware buffer underruns and fills the buffer with another block
77 // of audio.
78
CrasUnifiedStream(const AudioParameters & params,AudioManagerCras * manager)79 CrasUnifiedStream::CrasUnifiedStream(const AudioParameters& params,
80 AudioManagerCras* manager)
81 : client_(NULL),
82 stream_id_(0),
83 params_(params),
84 bytes_per_frame_(0),
85 is_playing_(false),
86 volume_(1.0),
87 manager_(manager),
88 source_callback_(NULL),
89 stream_direction_(CRAS_STREAM_OUTPUT) {
90 DCHECK(manager_);
91 DCHECK(params_.channels() > 0);
92
93 // Must have at least one input or output. If there are both they must be the
94 // same.
95 int input_channels = params_.input_channels();
96
97 if (input_channels) {
98 // A unified stream for input and output.
99 DCHECK(params_.channels() == input_channels);
100 stream_direction_ = CRAS_STREAM_UNIFIED;
101 input_bus_ = AudioBus::Create(input_channels,
102 params_.frames_per_buffer());
103 }
104
105 output_bus_ = AudioBus::Create(params);
106 }
107
~CrasUnifiedStream()108 CrasUnifiedStream::~CrasUnifiedStream() {
109 DCHECK(!is_playing_);
110 }
111
Open()112 bool CrasUnifiedStream::Open() {
113 // Sanity check input values.
114 if (params_.sample_rate() <= 0) {
115 LOG(WARNING) << "Unsupported audio frequency.";
116 return false;
117 }
118
119 if (alsa_util::BitsToFormat(params_.bits_per_sample()) ==
120 SND_PCM_FORMAT_UNKNOWN) {
121 LOG(WARNING) << "Unsupported pcm format";
122 return false;
123 }
124
125 // Create the client and connect to the CRAS server.
126 if (cras_client_create(&client_)) {
127 LOG(WARNING) << "Couldn't create CRAS client.\n";
128 client_ = NULL;
129 return false;
130 }
131
132 if (cras_client_connect(client_)) {
133 LOG(WARNING) << "Couldn't connect CRAS client.\n";
134 cras_client_destroy(client_);
135 client_ = NULL;
136 return false;
137 }
138
139 // Then start running the client.
140 if (cras_client_run_thread(client_)) {
141 LOG(WARNING) << "Couldn't run CRAS client.\n";
142 cras_client_destroy(client_);
143 client_ = NULL;
144 return false;
145 }
146
147 return true;
148 }
149
Close()150 void CrasUnifiedStream::Close() {
151 if (client_) {
152 cras_client_stop(client_);
153 cras_client_destroy(client_);
154 client_ = NULL;
155 }
156
157 // Signal to the manager that we're closed and can be removed.
158 // Should be last call in the method as it deletes "this".
159 manager_->ReleaseOutputStream(this);
160 }
161
Start(AudioSourceCallback * callback)162 void CrasUnifiedStream::Start(AudioSourceCallback* callback) {
163 CHECK(callback);
164
165 // Channel map to CRAS_CHANNEL, values in the same order of
166 // corresponding source in Chromium defined Channels.
167 static const int kChannelMap[] = {
168 CRAS_CH_FL,
169 CRAS_CH_FR,
170 CRAS_CH_FC,
171 CRAS_CH_LFE,
172 CRAS_CH_RL,
173 CRAS_CH_RR,
174 CRAS_CH_FLC,
175 CRAS_CH_FRC,
176 CRAS_CH_RC,
177 CRAS_CH_SL,
178 CRAS_CH_SR
179 };
180
181 source_callback_ = callback;
182
183 // Only start if we can enter the playing state.
184 if (is_playing_)
185 return;
186
187 // Prepare |audio_format| and |stream_params| for the stream we
188 // will create.
189 cras_audio_format* audio_format = cras_audio_format_create(
190 alsa_util::BitsToFormat(params_.bits_per_sample()),
191 params_.sample_rate(),
192 params_.channels());
193 if (!audio_format) {
194 LOG(WARNING) << "Error setting up audio parameters.";
195 callback->OnError(this);
196 return;
197 }
198
199 // Initialize channel layout to all -1 to indicate that none of
200 // the channels is set in the layout.
201 int8 layout[CRAS_CH_MAX] = {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 };
202
203 // Converts to CRAS defined channels. ChannelOrder will return -1
204 // for channels that does not present in params_.channel_layout().
205 for (size_t i = 0; i < arraysize(kChannelMap); ++i)
206 layout[kChannelMap[i]] = ChannelOrder(params_.channel_layout(),
207 static_cast<Channels>(i));
208
209 if (cras_audio_format_set_channel_layout(audio_format, layout)) {
210 LOG(WARNING) << "Error setting channel layout.";
211 callback->OnError(this);
212 return;
213 }
214
215 cras_stream_params* stream_params = cras_client_unified_params_create(
216 stream_direction_,
217 params_.frames_per_buffer(),
218 CRAS_STREAM_TYPE_DEFAULT,
219 0,
220 this,
221 CrasUnifiedStream::UnifiedCallback,
222 CrasUnifiedStream::StreamError,
223 audio_format);
224 if (!stream_params) {
225 LOG(WARNING) << "Error setting up stream parameters.";
226 callback->OnError(this);
227 cras_audio_format_destroy(audio_format);
228 return;
229 }
230
231 // Before starting the stream, save the number of bytes in a frame for use in
232 // the callback.
233 bytes_per_frame_ = cras_client_format_bytes_per_frame(audio_format);
234
235 // Adding the stream will start the audio callbacks requesting data.
236 if (cras_client_add_stream(client_, &stream_id_, stream_params) < 0) {
237 LOG(WARNING) << "Failed to add the stream";
238 callback->OnError(this);
239 cras_audio_format_destroy(audio_format);
240 cras_client_stream_params_destroy(stream_params);
241 return;
242 }
243
244 // Set initial volume.
245 cras_client_set_stream_volume(client_, stream_id_, volume_);
246
247 // Done with config params.
248 cras_audio_format_destroy(audio_format);
249 cras_client_stream_params_destroy(stream_params);
250
251 is_playing_ = true;
252 }
253
Stop()254 void CrasUnifiedStream::Stop() {
255 if (!client_)
256 return;
257
258 // Removing the stream from the client stops audio.
259 cras_client_rm_stream(client_, stream_id_);
260
261 is_playing_ = false;
262 }
263
SetVolume(double volume)264 void CrasUnifiedStream::SetVolume(double volume) {
265 if (!client_)
266 return;
267 volume_ = static_cast<float>(volume);
268 cras_client_set_stream_volume(client_, stream_id_, volume_);
269 }
270
GetVolume(double * volume)271 void CrasUnifiedStream::GetVolume(double* volume) {
272 *volume = volume_;
273 }
274
GetBytesLatency(const struct timespec & latency_ts)275 uint32 CrasUnifiedStream::GetBytesLatency(
276 const struct timespec& latency_ts) {
277 uint32 latency_usec;
278
279 // Treat negative latency (if we are too slow to render) as 0.
280 if (latency_ts.tv_sec < 0 || latency_ts.tv_nsec < 0) {
281 latency_usec = 0;
282 } else {
283 latency_usec = (latency_ts.tv_sec * base::Time::kMicrosecondsPerSecond) +
284 latency_ts.tv_nsec / base::Time::kNanosecondsPerMicrosecond;
285 }
286
287 double frames_latency =
288 latency_usec * params_.sample_rate() / base::Time::kMicrosecondsPerSecond;
289
290 return static_cast<unsigned int>(frames_latency * bytes_per_frame_);
291 }
292
293 // Static callback asking for samples.
UnifiedCallback(cras_client * client,cras_stream_id_t stream_id,uint8 * input_samples,uint8 * output_samples,unsigned int frames,const timespec * input_ts,const timespec * output_ts,void * arg)294 int CrasUnifiedStream::UnifiedCallback(cras_client* client,
295 cras_stream_id_t stream_id,
296 uint8* input_samples,
297 uint8* output_samples,
298 unsigned int frames,
299 const timespec* input_ts,
300 const timespec* output_ts,
301 void* arg) {
302 CrasUnifiedStream* me = static_cast<CrasUnifiedStream*>(arg);
303 return me->DispatchCallback(frames,
304 input_samples,
305 output_samples,
306 input_ts,
307 output_ts);
308 }
309
310 // Static callback for stream errors.
StreamError(cras_client * client,cras_stream_id_t stream_id,int err,void * arg)311 int CrasUnifiedStream::StreamError(cras_client* client,
312 cras_stream_id_t stream_id,
313 int err,
314 void* arg) {
315 CrasUnifiedStream* me = static_cast<CrasUnifiedStream*>(arg);
316 me->NotifyStreamError(err);
317 return 0;
318 }
319
320 // Calls the appropriate rendering function for this type of stream.
DispatchCallback(size_t frames,uint8 * input_samples,uint8 * output_samples,const timespec * input_ts,const timespec * output_ts)321 uint32 CrasUnifiedStream::DispatchCallback(size_t frames,
322 uint8* input_samples,
323 uint8* output_samples,
324 const timespec* input_ts,
325 const timespec* output_ts) {
326 switch (stream_direction_) {
327 case CRAS_STREAM_OUTPUT:
328 return WriteAudio(frames, output_samples, output_ts);
329 case CRAS_STREAM_INPUT:
330 NOTREACHED() << "CrasUnifiedStream doesn't support input streams.";
331 return 0;
332 case CRAS_STREAM_UNIFIED:
333 return ReadWriteAudio(frames, input_samples, output_samples,
334 input_ts, output_ts);
335 default:
336 break;
337 }
338
339 return 0;
340 }
341
342 // Note these are run from a real time thread, so don't waste cycles here.
ReadWriteAudio(size_t frames,uint8 * input_samples,uint8 * output_samples,const timespec * input_ts,const timespec * output_ts)343 uint32 CrasUnifiedStream::ReadWriteAudio(size_t frames,
344 uint8* input_samples,
345 uint8* output_samples,
346 const timespec* input_ts,
347 const timespec* output_ts) {
348 DCHECK_EQ(frames, static_cast<size_t>(output_bus_->frames()));
349 DCHECK(source_callback_);
350
351 uint32 bytes_per_sample = bytes_per_frame_ / params_.channels();
352 input_bus_->FromInterleaved(input_samples, frames, bytes_per_sample);
353
354 // Determine latency and pass that on to the source. We have the capture time
355 // of the first input sample and the playback time of the next audio sample
356 // passed from the audio server, add them together for total latency.
357 uint32 total_delay_bytes;
358 timespec latency_ts = {0, 0};
359 cras_client_calc_capture_latency(input_ts, &latency_ts);
360 total_delay_bytes = GetBytesLatency(latency_ts);
361 cras_client_calc_playback_latency(output_ts, &latency_ts);
362 total_delay_bytes += GetBytesLatency(latency_ts);
363
364 int frames_filled = source_callback_->OnMoreIOData(
365 input_bus_.get(),
366 output_bus_.get(),
367 AudioBuffersState(0, total_delay_bytes));
368
369 output_bus_->ToInterleaved(frames_filled, bytes_per_sample, output_samples);
370
371 return frames_filled;
372 }
373
WriteAudio(size_t frames,uint8 * buffer,const timespec * sample_ts)374 uint32 CrasUnifiedStream::WriteAudio(size_t frames,
375 uint8* buffer,
376 const timespec* sample_ts) {
377 DCHECK_EQ(frames, static_cast<size_t>(output_bus_->frames()));
378
379 // Determine latency and pass that on to the source.
380 timespec latency_ts = {0, 0};
381 cras_client_calc_playback_latency(sample_ts, &latency_ts);
382
383 int frames_filled = source_callback_->OnMoreData(
384 output_bus_.get(), AudioBuffersState(0, GetBytesLatency(latency_ts)));
385
386 // Note: If this ever changes to output raw float the data must be clipped and
387 // sanitized since it may come from an untrusted source such as NaCl.
388 output_bus_->ToInterleaved(
389 frames_filled, bytes_per_frame_ / params_.channels(), buffer);
390
391 return frames_filled;
392 }
393
NotifyStreamError(int err)394 void CrasUnifiedStream::NotifyStreamError(int err) {
395 // This will remove the stream from the client.
396 if (source_callback_)
397 source_callback_->OnError(this);
398 }
399
400 } // namespace media
401