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