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