cras_unified.cc revision eb525c5499e34cc9c4b825d6d9e75bb07cc06ace
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  LOG(ERROR) << "Unified Start";
172  // Prepare |audio_format| and |stream_params| for the stream we
173  // will create.
174  cras_audio_format* audio_format = cras_audio_format_create(
175      alsa_util::BitsToFormat(params_.bits_per_sample()),
176      params_.sample_rate(),
177      params_.channels());
178  if (!audio_format) {
179    LOG(WARNING) << "Error setting up audio parameters.";
180    callback->OnError(this);
181    return;
182  }
183
184  cras_stream_params* stream_params = cras_client_unified_params_create(
185      stream_direction_,
186      params_.frames_per_buffer(),
187      CRAS_STREAM_TYPE_DEFAULT,
188      0,
189      this,
190      CrasUnifiedStream::UnifiedCallback,
191      CrasUnifiedStream::StreamError,
192      audio_format);
193  if (!stream_params) {
194    LOG(WARNING) << "Error setting up stream parameters.";
195    callback->OnError(this);
196    cras_audio_format_destroy(audio_format);
197    return;
198  }
199
200  // Before starting the stream, save the number of bytes in a frame for use in
201  // the callback.
202  bytes_per_frame_ = cras_client_format_bytes_per_frame(audio_format);
203
204  // Adding the stream will start the audio callbacks requesting data.
205  if (cras_client_add_stream(client_, &stream_id_, stream_params) < 0) {
206    LOG(WARNING) << "Failed to add the stream";
207    callback->OnError(this);
208    cras_audio_format_destroy(audio_format);
209    cras_client_stream_params_destroy(stream_params);
210    return;
211  }
212
213  // Set initial volume.
214  cras_client_set_stream_volume(client_, stream_id_, volume_);
215
216  // Done with config params.
217  cras_audio_format_destroy(audio_format);
218  cras_client_stream_params_destroy(stream_params);
219
220  is_playing_ = true;
221}
222
223void CrasUnifiedStream::Stop() {
224  if (!client_)
225    return;
226
227  // Removing the stream from the client stops audio.
228  cras_client_rm_stream(client_, stream_id_);
229
230  is_playing_ = false;
231}
232
233void CrasUnifiedStream::SetVolume(double volume) {
234  if (!client_)
235    return;
236  volume_ = static_cast<float>(volume);
237  cras_client_set_stream_volume(client_, stream_id_, volume_);
238}
239
240void CrasUnifiedStream::GetVolume(double* volume) {
241  *volume = volume_;
242}
243
244uint32 CrasUnifiedStream::GetBytesLatency(
245    const struct timespec& latency_ts) {
246  uint32 latency_usec;
247
248  // Treat negative latency (if we are too slow to render) as 0.
249  if (latency_ts.tv_sec < 0 || latency_ts.tv_nsec < 0) {
250    latency_usec = 0;
251  } else {
252    latency_usec = (latency_ts.tv_sec * base::Time::kMicrosecondsPerSecond) +
253        latency_ts.tv_nsec / base::Time::kNanosecondsPerMicrosecond;
254  }
255
256  double frames_latency =
257      latency_usec * params_.sample_rate() / base::Time::kMicrosecondsPerSecond;
258
259  return static_cast<unsigned int>(frames_latency * bytes_per_frame_);
260}
261
262// Static callback asking for samples.
263int CrasUnifiedStream::UnifiedCallback(cras_client* client,
264                                       cras_stream_id_t stream_id,
265                                       uint8* input_samples,
266                                       uint8* output_samples,
267                                       unsigned int frames,
268                                       const timespec* input_ts,
269                                       const timespec* output_ts,
270                                       void* arg) {
271  CrasUnifiedStream* me = static_cast<CrasUnifiedStream*>(arg);
272  return me->DispatchCallback(frames,
273                              input_samples,
274                              output_samples,
275                              input_ts,
276                              output_ts);
277}
278
279// Static callback for stream errors.
280int CrasUnifiedStream::StreamError(cras_client* client,
281                                   cras_stream_id_t stream_id,
282                                   int err,
283                                   void* arg) {
284  CrasUnifiedStream* me = static_cast<CrasUnifiedStream*>(arg);
285  me->NotifyStreamError(err);
286  return 0;
287}
288
289// Calls the appropriate rendering function for this type of stream.
290uint32 CrasUnifiedStream::DispatchCallback(size_t frames,
291                                           uint8* input_samples,
292                                           uint8* output_samples,
293                                           const timespec* input_ts,
294                                           const timespec* output_ts) {
295  switch (stream_direction_) {
296    case CRAS_STREAM_OUTPUT:
297      return WriteAudio(frames, output_samples, output_ts);
298    case CRAS_STREAM_INPUT:
299      NOTREACHED() << "CrasUnifiedStream doesn't support input streams.";
300      return 0;
301    case CRAS_STREAM_UNIFIED:
302      return ReadWriteAudio(frames, input_samples, output_samples,
303                            input_ts, output_ts);
304    default:
305      break;
306  }
307
308  return 0;
309}
310
311// Note these are run from a real time thread, so don't waste cycles here.
312uint32 CrasUnifiedStream::ReadWriteAudio(size_t frames,
313                                         uint8* input_samples,
314                                         uint8* output_samples,
315                                         const timespec* input_ts,
316                                         const timespec* output_ts) {
317  DCHECK_EQ(frames, static_cast<size_t>(output_bus_->frames()));
318  DCHECK(source_callback_);
319
320  uint32 bytes_per_sample = bytes_per_frame_ / params_.channels();
321  input_bus_->FromInterleaved(input_samples, frames, bytes_per_sample);
322
323  // Determine latency and pass that on to the source.  We have the capture time
324  // of the first input sample and the playback time of the next audio sample
325  // passed from the audio server, add them together for total latency.
326  uint32 total_delay_bytes;
327  timespec latency_ts  = {0, 0};
328  cras_client_calc_capture_latency(input_ts, &latency_ts);
329  total_delay_bytes = GetBytesLatency(latency_ts);
330  cras_client_calc_playback_latency(output_ts, &latency_ts);
331  total_delay_bytes += GetBytesLatency(latency_ts);
332
333  int frames_filled = source_callback_->OnMoreIOData(
334      input_bus_.get(),
335      output_bus_.get(),
336      AudioBuffersState(0, total_delay_bytes));
337
338  output_bus_->ToInterleaved(frames_filled, bytes_per_sample, output_samples);
339
340  return frames_filled;
341}
342
343uint32 CrasUnifiedStream::WriteAudio(size_t frames,
344                                     uint8* buffer,
345                                     const timespec* sample_ts) {
346  DCHECK_EQ(frames, static_cast<size_t>(output_bus_->frames()));
347
348  // Determine latency and pass that on to the source.
349  timespec latency_ts  = {0, 0};
350  cras_client_calc_playback_latency(sample_ts, &latency_ts);
351
352  int frames_filled = source_callback_->OnMoreData(
353      output_bus_.get(), AudioBuffersState(0, GetBytesLatency(latency_ts)));
354
355  // Note: If this ever changes to output raw float the data must be clipped and
356  // sanitized since it may come from an untrusted source such as NaCl.
357  output_bus_->ToInterleaved(
358      frames_filled, bytes_per_frame_ / params_.channels(), buffer);
359
360  return frames_filled;
361}
362
363void CrasUnifiedStream::NotifyStreamError(int err) {
364  // This will remove the stream from the client.
365  if (source_callback_)
366    source_callback_->OnError(this);
367}
368
369}  // namespace media
370