Separate Simple Backend creation from initialization.
[chromium-blink-merge.git] / media / audio / cras / cras_unified.cc
blobe88a06962a0764781171e81fc86098807a6940b9
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.
5 #include "media/audio/cras/cras_unified.h"
7 #include <cras_client.h>
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"
15 namespace media {
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.
29 // For output-only streams, a unified stream is created with 0 input channels.
31 // Simplified data flow for unified streams:
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 // | |
53 // Simplified data flow for output only streams:
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 // | |
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.
80 CrasUnifiedStream::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 input_bus_(NULL),
91 output_bus_(NULL),
92 stream_direction_(CRAS_STREAM_OUTPUT) {
93 DCHECK(manager_);
94 DCHECK(params_.channels() > 0);
96 // Must have at least one input or output. If there are both they must be the
97 // same.
98 int input_channels = params_.input_channels();
100 if (input_channels) {
101 // A unified stream for input and output.
102 DCHECK(params_.channels() == input_channels);
103 stream_direction_ = CRAS_STREAM_UNIFIED;
104 input_bus_ = AudioBus::Create(input_channels,
105 params_.frames_per_buffer());
108 output_bus_ = AudioBus::Create(params);
111 CrasUnifiedStream::~CrasUnifiedStream() {
112 DCHECK(!is_playing_);
115 bool CrasUnifiedStream::Open() {
116 // Sanity check input values.
117 if (params_.sample_rate() <= 0) {
118 LOG(WARNING) << "Unsupported audio frequency.";
119 return false;
122 if (alsa_util::BitsToFormat(params_.bits_per_sample()) ==
123 SND_PCM_FORMAT_UNKNOWN) {
124 LOG(WARNING) << "Unsupported pcm format";
125 return false;
128 // Create the client and connect to the CRAS server.
129 if (cras_client_create(&client_)) {
130 LOG(WARNING) << "Couldn't create CRAS client.\n";
131 client_ = NULL;
132 return false;
135 if (cras_client_connect(client_)) {
136 LOG(WARNING) << "Couldn't connect CRAS client.\n";
137 cras_client_destroy(client_);
138 client_ = NULL;
139 return false;
142 // Then start running the client.
143 if (cras_client_run_thread(client_)) {
144 LOG(WARNING) << "Couldn't run CRAS client.\n";
145 cras_client_destroy(client_);
146 client_ = NULL;
147 return false;
150 return true;
153 void CrasUnifiedStream::Close() {
154 if (client_) {
155 cras_client_stop(client_);
156 cras_client_destroy(client_);
157 client_ = NULL;
160 // Signal to the manager that we're closed and can be removed.
161 // Should be last call in the method as it deletes "this".
162 manager_->ReleaseOutputStream(this);
165 void CrasUnifiedStream::Start(AudioSourceCallback* callback) {
166 CHECK(callback);
167 source_callback_ = callback;
169 // Only start if we can enter the playing state.
170 if (is_playing_)
171 return;
173 LOG(ERROR) << "Unified Start";
174 // Prepare |audio_format| and |stream_params| for the stream we
175 // will create.
176 cras_audio_format* audio_format = cras_audio_format_create(
177 alsa_util::BitsToFormat(params_.bits_per_sample()),
178 params_.sample_rate(),
179 params_.channels());
180 if (!audio_format) {
181 LOG(WARNING) << "Error setting up audio parameters.";
182 callback->OnError(this);
183 return;
186 cras_stream_params* stream_params = cras_client_unified_params_create(
187 stream_direction_,
188 params_.frames_per_buffer(),
189 CRAS_STREAM_TYPE_DEFAULT,
191 this,
192 CrasUnifiedStream::UnifiedCallback,
193 CrasUnifiedStream::StreamError,
194 audio_format);
195 if (!stream_params) {
196 LOG(WARNING) << "Error setting up stream parameters.";
197 callback->OnError(this);
198 cras_audio_format_destroy(audio_format);
199 return;
202 // Before starting the stream, save the number of bytes in a frame for use in
203 // the callback.
204 bytes_per_frame_ = cras_client_format_bytes_per_frame(audio_format);
206 // Adding the stream will start the audio callbacks requesting data.
207 if (cras_client_add_stream(client_, &stream_id_, stream_params) < 0) {
208 LOG(WARNING) << "Failed to add the stream";
209 callback->OnError(this);
210 cras_audio_format_destroy(audio_format);
211 cras_client_stream_params_destroy(stream_params);
212 return;
215 // Set initial volume.
216 cras_client_set_stream_volume(client_, stream_id_, volume_);
218 // Done with config params.
219 cras_audio_format_destroy(audio_format);
220 cras_client_stream_params_destroy(stream_params);
222 is_playing_ = true;
225 void CrasUnifiedStream::Stop() {
226 if (!client_)
227 return;
229 // Removing the stream from the client stops audio.
230 cras_client_rm_stream(client_, stream_id_);
232 is_playing_ = false;
235 void CrasUnifiedStream::SetVolume(double volume) {
236 if (!client_)
237 return;
238 volume_ = static_cast<float>(volume);
239 cras_client_set_stream_volume(client_, stream_id_, volume_);
242 void CrasUnifiedStream::GetVolume(double* volume) {
243 *volume = volume_;
246 uint32 CrasUnifiedStream::GetBytesLatency(
247 const struct timespec& latency_ts) {
248 uint32 latency_usec;
250 // Treat negative latency (if we are too slow to render) as 0.
251 if (latency_ts.tv_sec < 0 || latency_ts.tv_nsec < 0) {
252 latency_usec = 0;
253 } else {
254 latency_usec = (latency_ts.tv_sec * base::Time::kMicrosecondsPerSecond) +
255 latency_ts.tv_nsec / base::Time::kNanosecondsPerMicrosecond;
258 double frames_latency =
259 latency_usec * params_.sample_rate() / base::Time::kMicrosecondsPerSecond;
261 return static_cast<unsigned int>(frames_latency * bytes_per_frame_);
264 // Static callback asking for samples.
265 int CrasUnifiedStream::UnifiedCallback(cras_client* client,
266 cras_stream_id_t stream_id,
267 uint8* input_samples,
268 uint8* output_samples,
269 unsigned int frames,
270 const timespec* input_ts,
271 const timespec* output_ts,
272 void* arg) {
273 CrasUnifiedStream* me = static_cast<CrasUnifiedStream*>(arg);
274 return me->DispatchCallback(frames,
275 input_samples,
276 output_samples,
277 input_ts,
278 output_ts);
281 // Static callback for stream errors.
282 int CrasUnifiedStream::StreamError(cras_client* client,
283 cras_stream_id_t stream_id,
284 int err,
285 void* arg) {
286 CrasUnifiedStream* me = static_cast<CrasUnifiedStream*>(arg);
287 me->NotifyStreamError(err);
288 return 0;
291 // Calls the appropriate rendering function for this type of stream.
292 uint32 CrasUnifiedStream::DispatchCallback(size_t frames,
293 uint8* input_samples,
294 uint8* output_samples,
295 const timespec* input_ts,
296 const timespec* output_ts) {
297 switch (stream_direction_) {
298 case CRAS_STREAM_OUTPUT:
299 return WriteAudio(frames, output_samples, output_ts);
300 case CRAS_STREAM_INPUT:
301 NOTREACHED() << "CrasUnifiedStream doesn't support input streams.";
302 return 0;
303 case CRAS_STREAM_UNIFIED:
304 return ReadWriteAudio(frames, input_samples, output_samples,
305 input_ts, output_ts);
308 return 0;
311 // Note these are run from a real time thread, so don't waste cycles here.
312 uint32 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_);
320 uint32 bytes_per_sample = bytes_per_frame_ / params_.channels();
321 input_bus_->FromInterleaved(input_samples, frames, bytes_per_sample);
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);
333 int frames_filled = source_callback_->OnMoreIOData(
334 input_bus_.get(),
335 output_bus_.get(),
336 AudioBuffersState(0, total_delay_bytes));
338 output_bus_->ToInterleaved(frames_filled, bytes_per_sample, output_samples);
340 return frames_filled;
343 uint32 CrasUnifiedStream::WriteAudio(size_t frames,
344 uint8* buffer,
345 const timespec* sample_ts) {
346 DCHECK_EQ(frames, static_cast<size_t>(output_bus_->frames()));
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);
352 int frames_filled = source_callback_->OnMoreData(
353 output_bus_.get(), AudioBuffersState(0, GetBytesLatency(latency_ts)));
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);
360 return frames_filled;
363 void CrasUnifiedStream::NotifyStreamError(int err) {
364 // This will remove the stream from the client.
365 if (source_callback_)
366 source_callback_->OnError(this);
369 } // namespace media