cras_unified.cc revision f2477e01787aa58f445919b809d89e252beef54f
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
14namespace 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
79CrasUnifiedStream::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
108CrasUnifiedStream::~CrasUnifiedStream() {
109  DCHECK(!is_playing_);
110}
111
112bool 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
150void 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
162void 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
254void 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
264void 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
271void CrasUnifiedStream::GetVolume(double* volume) {
272  *volume = volume_;
273}
274
275uint32 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.
294int 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.
311int 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.
321uint32 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.
343uint32 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
374uint32 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
394void 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