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
10 namespace 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
CrasUnifiedStream(const AudioParameters & params,AudioManagerCras * manager)75 CrasUnifiedStream::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
~CrasUnifiedStream()104 CrasUnifiedStream::~CrasUnifiedStream() {
105 DCHECK(!is_playing_);
106 }
107
Open()108 bool 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
Close()146 void 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
Start(AudioSourceCallback * callback)158 void 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
Stop()250 void 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
SetVolume(double volume)260 void 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
GetVolume(double * volume)267 void CrasUnifiedStream::GetVolume(double* volume) {
268 *volume = volume_;
269 }
270
GetBytesLatency(const struct timespec & latency_ts)271 uint32 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.
UnifiedCallback(cras_client * client,cras_stream_id_t stream_id,uint8 * input_samples,uint8 * output_samples,unsigned int frames,const timespec * input_ts,const timespec * output_ts,void * arg)290 int 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.
StreamError(cras_client * client,cras_stream_id_t stream_id,int err,void * arg)307 int 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.
DispatchCallback(size_t frames,uint8 * input_samples,uint8 * output_samples,const timespec * input_ts,const timespec * output_ts)317 uint32 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.
ReadWriteAudio(size_t frames,uint8 * input_samples,uint8 * output_samples,const timespec * input_ts,const timespec * output_ts)339 uint32 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
WriteAudio(size_t frames,uint8 * buffer,const timespec * sample_ts)369 uint32 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
NotifyStreamError(int err)389 void 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