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// Simplified data flow for output only streams:
25//
26//   +-------------+                  +------------------+
27//   | CRAS Server |                  | Chrome Client    |
28//   +------+------+    Add Stream    +---------+--------+
29//          |<----------------------------------|
30//          |                                   |
31//          | Near out of samples, request more |
32//          |---------------------------------->|
33//          |                                   |  UnifiedCallback()
34//          |                                   |  WriteAudio()
35//          |                                   |
36//          |  buffer_frames written to shm     |
37//          |<----------------------------------|
38//          |                                   |
39//         ...  Repeats for each block.        ...
40//          |                                   |
41//          |                                   |
42//          |  Remove stream                    |
43//          |<----------------------------------|
44//          |                                   |
45//
46// For Unified streams the Chrome client is notified whenever buffer_frames have
47// been captured.  For Output streams the client is notified a few milliseconds
48// before the hardware buffer underruns and fills the buffer with another block
49// of audio.
50
51CrasUnifiedStream::CrasUnifiedStream(const AudioParameters& params,
52                                     AudioManagerCras* manager)
53    : client_(NULL),
54      stream_id_(0),
55      params_(params),
56      bytes_per_frame_(0),
57      is_playing_(false),
58      volume_(1.0),
59      manager_(manager),
60      source_callback_(NULL),
61      stream_direction_(CRAS_STREAM_OUTPUT) {
62  DCHECK(manager_);
63  DCHECK(params_.channels()  > 0);
64
65  output_bus_ = AudioBus::Create(params);
66}
67
68CrasUnifiedStream::~CrasUnifiedStream() {
69  DCHECK(!is_playing_);
70}
71
72bool CrasUnifiedStream::Open() {
73  // Sanity check input values.
74  if (params_.sample_rate() <= 0) {
75    LOG(WARNING) << "Unsupported audio frequency.";
76    return false;
77  }
78
79  if (AudioManagerCras::BitsToFormat(params_.bits_per_sample()) ==
80      SND_PCM_FORMAT_UNKNOWN) {
81    LOG(WARNING) << "Unsupported pcm format";
82    return false;
83  }
84
85  // Create the client and connect to the CRAS server.
86  if (cras_client_create(&client_)) {
87    LOG(WARNING) << "Couldn't create CRAS client.\n";
88    client_ = NULL;
89    return false;
90  }
91
92  if (cras_client_connect(client_)) {
93    LOG(WARNING) << "Couldn't connect CRAS client.\n";
94    cras_client_destroy(client_);
95    client_ = NULL;
96    return false;
97  }
98
99  // Then start running the client.
100  if (cras_client_run_thread(client_)) {
101    LOG(WARNING) << "Couldn't run CRAS client.\n";
102    cras_client_destroy(client_);
103    client_ = NULL;
104    return false;
105  }
106
107  return true;
108}
109
110void CrasUnifiedStream::Close() {
111  if (client_) {
112    cras_client_stop(client_);
113    cras_client_destroy(client_);
114    client_ = NULL;
115  }
116
117  // Signal to the manager that we're closed and can be removed.
118  // Should be last call in the method as it deletes "this".
119  manager_->ReleaseOutputStream(this);
120}
121
122void CrasUnifiedStream::Start(AudioSourceCallback* callback) {
123  CHECK(callback);
124
125  // Channel map to CRAS_CHANNEL, values in the same order of
126  // corresponding source in Chromium defined Channels.
127  static const int kChannelMap[] = {
128    CRAS_CH_FL,
129    CRAS_CH_FR,
130    CRAS_CH_FC,
131    CRAS_CH_LFE,
132    CRAS_CH_RL,
133    CRAS_CH_RR,
134    CRAS_CH_FLC,
135    CRAS_CH_FRC,
136    CRAS_CH_RC,
137    CRAS_CH_SL,
138    CRAS_CH_SR
139  };
140
141  source_callback_ = callback;
142
143  // Only start if we can enter the playing state.
144  if (is_playing_)
145    return;
146
147  // Prepare |audio_format| and |stream_params| for the stream we
148  // will create.
149  cras_audio_format* audio_format = cras_audio_format_create(
150      AudioManagerCras::BitsToFormat(params_.bits_per_sample()),
151      params_.sample_rate(),
152      params_.channels());
153  if (!audio_format) {
154    LOG(WARNING) << "Error setting up audio parameters.";
155    callback->OnError(this);
156    return;
157  }
158
159  // Initialize channel layout to all -1 to indicate that none of
160  // the channels is set in the layout.
161  int8 layout[CRAS_CH_MAX] = {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 };
162
163  // Converts to CRAS defined channels. ChannelOrder will return -1
164  // for channels that does not present in params_.channel_layout().
165  for (size_t i = 0; i < arraysize(kChannelMap); ++i)
166    layout[kChannelMap[i]] = ChannelOrder(params_.channel_layout(),
167                                          static_cast<Channels>(i));
168
169  if (cras_audio_format_set_channel_layout(audio_format, layout)) {
170    LOG(WARNING) << "Error setting channel layout.";
171    callback->OnError(this);
172    return;
173  }
174
175  cras_stream_params* stream_params = cras_client_unified_params_create(
176      stream_direction_,
177      params_.frames_per_buffer(),
178      CRAS_STREAM_TYPE_DEFAULT,
179      0,
180      this,
181      CrasUnifiedStream::UnifiedCallback,
182      CrasUnifiedStream::StreamError,
183      audio_format);
184  if (!stream_params) {
185    LOG(WARNING) << "Error setting up stream parameters.";
186    callback->OnError(this);
187    cras_audio_format_destroy(audio_format);
188    return;
189  }
190
191  // Before starting the stream, save the number of bytes in a frame for use in
192  // the callback.
193  bytes_per_frame_ = cras_client_format_bytes_per_frame(audio_format);
194
195  // Adding the stream will start the audio callbacks requesting data.
196  if (cras_client_add_stream(client_, &stream_id_, stream_params) < 0) {
197    LOG(WARNING) << "Failed to add the stream";
198    callback->OnError(this);
199    cras_audio_format_destroy(audio_format);
200    cras_client_stream_params_destroy(stream_params);
201    return;
202  }
203
204  // Set initial volume.
205  cras_client_set_stream_volume(client_, stream_id_, volume_);
206
207  // Done with config params.
208  cras_audio_format_destroy(audio_format);
209  cras_client_stream_params_destroy(stream_params);
210
211  is_playing_ = true;
212}
213
214void CrasUnifiedStream::Stop() {
215  if (!client_)
216    return;
217
218  // Removing the stream from the client stops audio.
219  cras_client_rm_stream(client_, stream_id_);
220
221  is_playing_ = false;
222}
223
224void CrasUnifiedStream::SetVolume(double volume) {
225  if (!client_)
226    return;
227  volume_ = static_cast<float>(volume);
228  cras_client_set_stream_volume(client_, stream_id_, volume_);
229}
230
231void CrasUnifiedStream::GetVolume(double* volume) {
232  *volume = volume_;
233}
234
235uint32 CrasUnifiedStream::GetBytesLatency(
236    const struct timespec& latency_ts) {
237  uint32 latency_usec;
238
239  // Treat negative latency (if we are too slow to render) as 0.
240  if (latency_ts.tv_sec < 0 || latency_ts.tv_nsec < 0) {
241    latency_usec = 0;
242  } else {
243    latency_usec = (latency_ts.tv_sec * base::Time::kMicrosecondsPerSecond) +
244        latency_ts.tv_nsec / base::Time::kNanosecondsPerMicrosecond;
245  }
246
247  double frames_latency =
248      latency_usec * params_.sample_rate() / base::Time::kMicrosecondsPerSecond;
249
250  return static_cast<unsigned int>(frames_latency * bytes_per_frame_);
251}
252
253// Static callback asking for samples.
254int CrasUnifiedStream::UnifiedCallback(cras_client* client,
255                                       cras_stream_id_t stream_id,
256                                       uint8* input_samples,
257                                       uint8* output_samples,
258                                       unsigned int frames,
259                                       const timespec* input_ts,
260                                       const timespec* output_ts,
261                                       void* arg) {
262  CrasUnifiedStream* me = static_cast<CrasUnifiedStream*>(arg);
263  return me->DispatchCallback(frames,
264                              input_samples,
265                              output_samples,
266                              input_ts,
267                              output_ts);
268}
269
270// Static callback for stream errors.
271int CrasUnifiedStream::StreamError(cras_client* client,
272                                   cras_stream_id_t stream_id,
273                                   int err,
274                                   void* arg) {
275  CrasUnifiedStream* me = static_cast<CrasUnifiedStream*>(arg);
276  me->NotifyStreamError(err);
277  return 0;
278}
279
280// Calls the appropriate rendering function for this type of stream.
281uint32 CrasUnifiedStream::DispatchCallback(size_t frames,
282                                           uint8* input_samples,
283                                           uint8* output_samples,
284                                           const timespec* input_ts,
285                                           const timespec* output_ts) {
286  switch (stream_direction_) {
287    case CRAS_STREAM_OUTPUT:
288      return WriteAudio(frames, output_samples, output_ts);
289    case CRAS_STREAM_INPUT:
290      NOTREACHED() << "CrasUnifiedStream doesn't support input streams.";
291      return 0;
292    default:
293      break;
294  }
295
296  return 0;
297}
298
299uint32 CrasUnifiedStream::WriteAudio(size_t frames,
300                                     uint8* buffer,
301                                     const timespec* sample_ts) {
302  DCHECK_EQ(frames, static_cast<size_t>(output_bus_->frames()));
303
304  // Determine latency and pass that on to the source.
305  timespec latency_ts  = {0, 0};
306  cras_client_calc_playback_latency(sample_ts, &latency_ts);
307
308  int frames_filled = source_callback_->OnMoreData(
309      output_bus_.get(), AudioBuffersState(0, GetBytesLatency(latency_ts)));
310
311  // Note: If this ever changes to output raw float the data must be clipped and
312  // sanitized since it may come from an untrusted source such as NaCl.
313  output_bus_->ToInterleaved(
314      frames_filled, bytes_per_frame_ / params_.channels(), buffer);
315
316  return frames_filled;
317}
318
319void CrasUnifiedStream::NotifyStreamError(int err) {
320  // This will remove the stream from the client.
321  if (source_callback_)
322    source_callback_->OnError(this);
323}
324
325}  // namespace media
326