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 "base/logging.h"
8 #include "media/audio/cras/audio_manager_cras.h"
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.
24 // For output-only streams, a unified stream is created with 0 input channels.
26 // Simplified data flow for unified streams:
28 // +-------------+ +------------------+
29 // | CRAS Server | | Chrome Client |
30 // +------+------+ Add Stream +---------+--------+
31 // |<----------------------------------|
33 // | buffer_frames captured to shm |
34 // |---------------------------------->|
35 // | | UnifiedCallback()
36 // | | ReadWriteAudio()
38 // | buffer_frames written to shm |
39 // |<----------------------------------|
41 // ... Repeats for each block. ...
45 // |<----------------------------------|
48 // Simplified data flow for output only streams:
50 // +-------------+ +------------------+
51 // | CRAS Server | | Chrome Client |
52 // +------+------+ Add Stream +---------+--------+
53 // |<----------------------------------|
55 // | Near out of samples, request more |
56 // |---------------------------------->|
57 // | | UnifiedCallback()
60 // | buffer_frames written to shm |
61 // |<----------------------------------|
63 // ... Repeats for each block. ...
67 // |<----------------------------------|
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
75 CrasUnifiedStream::CrasUnifiedStream(const AudioParameters
& params
,
76 AudioManagerCras
* manager
)
84 source_callback_(NULL
),
85 stream_direction_(CRAS_STREAM_OUTPUT
) {
87 DCHECK(params_
.channels() > 0);
89 // Must have at least one input or output. If there are both they must be the
91 int input_channels
= params_
.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());
101 output_bus_
= AudioBus::Create(params
);
104 CrasUnifiedStream::~CrasUnifiedStream() {
105 DCHECK(!is_playing_
);
108 bool CrasUnifiedStream::Open() {
109 // Sanity check input values.
110 if (params_
.sample_rate() <= 0) {
111 LOG(WARNING
) << "Unsupported audio frequency.";
115 if (AudioManagerCras::BitsToFormat(params_
.bits_per_sample()) ==
116 SND_PCM_FORMAT_UNKNOWN
) {
117 LOG(WARNING
) << "Unsupported pcm format";
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";
128 if (cras_client_connect(client_
)) {
129 LOG(WARNING
) << "Couldn't connect CRAS client.\n";
130 cras_client_destroy(client_
);
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_
);
146 void CrasUnifiedStream::Close() {
148 cras_client_stop(client_
);
149 cras_client_destroy(client_
);
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);
158 void CrasUnifiedStream::Start(AudioSourceCallback
* callback
) {
161 // Channel map to CRAS_CHANNEL, values in the same order of
162 // corresponding source in Chromium defined Channels.
163 static const int kChannelMap
[] = {
177 source_callback_
= callback
;
179 // Only start if we can enter the playing state.
183 // Prepare |audio_format| and |stream_params| for the stream we
185 cras_audio_format
* audio_format
= cras_audio_format_create(
186 AudioManagerCras::BitsToFormat(params_
.bits_per_sample()),
187 params_
.sample_rate(),
190 LOG(WARNING
) << "Error setting up audio parameters.";
191 callback
->OnError(this);
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 };
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
));
205 if (cras_audio_format_set_channel_layout(audio_format
, layout
)) {
206 LOG(WARNING
) << "Error setting channel layout.";
207 callback
->OnError(this);
211 cras_stream_params
* stream_params
= cras_client_unified_params_create(
213 params_
.frames_per_buffer(),
214 CRAS_STREAM_TYPE_DEFAULT
,
217 CrasUnifiedStream::UnifiedCallback
,
218 CrasUnifiedStream::StreamError
,
220 if (!stream_params
) {
221 LOG(WARNING
) << "Error setting up stream parameters.";
222 callback
->OnError(this);
223 cras_audio_format_destroy(audio_format
);
227 // Before starting the stream, save the number of bytes in a frame for use in
229 bytes_per_frame_
= cras_client_format_bytes_per_frame(audio_format
);
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
);
240 // Set initial volume.
241 cras_client_set_stream_volume(client_
, stream_id_
, volume_
);
243 // Done with config params.
244 cras_audio_format_destroy(audio_format
);
245 cras_client_stream_params_destroy(stream_params
);
250 void CrasUnifiedStream::Stop() {
254 // Removing the stream from the client stops audio.
255 cras_client_rm_stream(client_
, stream_id_
);
260 void CrasUnifiedStream::SetVolume(double volume
) {
263 volume_
= static_cast<float>(volume
);
264 cras_client_set_stream_volume(client_
, stream_id_
, volume_
);
267 void CrasUnifiedStream::GetVolume(double* volume
) {
271 uint32
CrasUnifiedStream::GetBytesLatency(
272 const struct timespec
& latency_ts
) {
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) {
279 latency_usec
= (latency_ts
.tv_sec
* base::Time::kMicrosecondsPerSecond
) +
280 latency_ts
.tv_nsec
/ base::Time::kNanosecondsPerMicrosecond
;
283 double frames_latency
=
284 latency_usec
* params_
.sample_rate() / base::Time::kMicrosecondsPerSecond
;
286 return static_cast<unsigned int>(frames_latency
* bytes_per_frame_
);
289 // Static callback asking for samples.
290 int CrasUnifiedStream::UnifiedCallback(cras_client
* client
,
291 cras_stream_id_t stream_id
,
292 uint8
* input_samples
,
293 uint8
* output_samples
,
295 const timespec
* input_ts
,
296 const timespec
* output_ts
,
298 CrasUnifiedStream
* me
= static_cast<CrasUnifiedStream
*>(arg
);
299 return me
->DispatchCallback(frames
,
306 // Static callback for stream errors.
307 int CrasUnifiedStream::StreamError(cras_client
* client
,
308 cras_stream_id_t stream_id
,
311 CrasUnifiedStream
* me
= static_cast<CrasUnifiedStream
*>(arg
);
312 me
->NotifyStreamError(err
);
316 // Calls the appropriate rendering function for this type of stream.
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.";
328 case CRAS_STREAM_UNIFIED
:
329 return ReadWriteAudio(frames
, input_samples
, output_samples
,
330 input_ts
, output_ts
);
338 // Note these are run from a real time thread, so don't waste cycles here.
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_
);
347 uint32 bytes_per_sample
= bytes_per_frame_
/ params_
.channels();
348 input_bus_
->FromInterleaved(input_samples
, frames
, bytes_per_sample
);
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
);
360 int frames_filled
= source_callback_
->OnMoreData(
362 AudioBuffersState(0, total_delay_bytes
));
364 output_bus_
->ToInterleaved(frames_filled
, bytes_per_sample
, output_samples
);
366 return frames_filled
;
369 uint32
CrasUnifiedStream::WriteAudio(size_t frames
,
371 const timespec
* sample_ts
) {
372 DCHECK_EQ(frames
, static_cast<size_t>(output_bus_
->frames()));
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
);
378 int frames_filled
= source_callback_
->OnMoreData(
379 output_bus_
.get(), AudioBuffersState(0, GetBytesLatency(latency_ts
)));
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
);
386 return frames_filled
;
389 void CrasUnifiedStream::NotifyStreamError(int err
) {
390 // This will remove the stream from the client.
391 if (source_callback_
)
392 source_callback_
->OnError(this);