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 // Simplified data flow for output only streams:
26 // +-------------+ +------------------+
27 // | CRAS Server | | Chrome Client |
28 // +------+------+ Add Stream +---------+--------+
29 // |<----------------------------------|
31 // | Near out of samples, request more |
32 // |---------------------------------->|
33 // | | UnifiedCallback()
36 // | buffer_frames written to shm |
37 // |<----------------------------------|
39 // ... Repeats for each block. ...
43 // |<----------------------------------|
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
51 CrasUnifiedStream::CrasUnifiedStream(const AudioParameters
& params
,
52 AudioManagerCras
* manager
)
60 source_callback_(NULL
),
61 stream_direction_(CRAS_STREAM_OUTPUT
) {
63 DCHECK(params_
.channels() > 0);
65 output_bus_
= AudioBus::Create(params
);
68 CrasUnifiedStream::~CrasUnifiedStream() {
72 bool CrasUnifiedStream::Open() {
73 // Sanity check input values.
74 if (params_
.sample_rate() <= 0) {
75 LOG(WARNING
) << "Unsupported audio frequency.";
79 if (AudioManagerCras::BitsToFormat(params_
.bits_per_sample()) ==
80 SND_PCM_FORMAT_UNKNOWN
) {
81 LOG(WARNING
) << "Unsupported pcm format";
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";
92 if (cras_client_connect(client_
)) {
93 LOG(WARNING
) << "Couldn't connect CRAS client.\n";
94 cras_client_destroy(client_
);
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_
);
110 void CrasUnifiedStream::Close() {
112 cras_client_stop(client_
);
113 cras_client_destroy(client_
);
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);
122 void CrasUnifiedStream::Start(AudioSourceCallback
* callback
) {
125 // Channel map to CRAS_CHANNEL, values in the same order of
126 // corresponding source in Chromium defined Channels.
127 static const int kChannelMap
[] = {
141 source_callback_
= callback
;
143 // Only start if we can enter the playing state.
147 // Prepare |audio_format| and |stream_params| for the stream we
149 cras_audio_format
* audio_format
= cras_audio_format_create(
150 AudioManagerCras::BitsToFormat(params_
.bits_per_sample()),
151 params_
.sample_rate(),
154 LOG(WARNING
) << "Error setting up audio parameters.";
155 callback
->OnError(this);
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 };
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
));
169 if (cras_audio_format_set_channel_layout(audio_format
, layout
)) {
170 LOG(WARNING
) << "Error setting channel layout.";
171 callback
->OnError(this);
175 cras_stream_params
* stream_params
= cras_client_unified_params_create(
177 params_
.frames_per_buffer(),
178 CRAS_STREAM_TYPE_DEFAULT
,
181 CrasUnifiedStream::UnifiedCallback
,
182 CrasUnifiedStream::StreamError
,
184 if (!stream_params
) {
185 LOG(WARNING
) << "Error setting up stream parameters.";
186 callback
->OnError(this);
187 cras_audio_format_destroy(audio_format
);
191 // Before starting the stream, save the number of bytes in a frame for use in
193 bytes_per_frame_
= cras_client_format_bytes_per_frame(audio_format
);
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
);
204 // Set initial volume.
205 cras_client_set_stream_volume(client_
, stream_id_
, volume_
);
207 // Done with config params.
208 cras_audio_format_destroy(audio_format
);
209 cras_client_stream_params_destroy(stream_params
);
214 void CrasUnifiedStream::Stop() {
218 // Removing the stream from the client stops audio.
219 cras_client_rm_stream(client_
, stream_id_
);
224 void CrasUnifiedStream::SetVolume(double volume
) {
227 volume_
= static_cast<float>(volume
);
228 cras_client_set_stream_volume(client_
, stream_id_
, volume_
);
231 void CrasUnifiedStream::GetVolume(double* volume
) {
235 uint32
CrasUnifiedStream::GetBytesLatency(
236 const struct timespec
& latency_ts
) {
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) {
243 latency_usec
= (latency_ts
.tv_sec
* base::Time::kMicrosecondsPerSecond
) +
244 latency_ts
.tv_nsec
/ base::Time::kNanosecondsPerMicrosecond
;
247 double frames_latency
=
248 latency_usec
* params_
.sample_rate() / base::Time::kMicrosecondsPerSecond
;
250 return static_cast<unsigned int>(frames_latency
* bytes_per_frame_
);
253 // Static callback asking for samples.
254 int CrasUnifiedStream::UnifiedCallback(cras_client
* client
,
255 cras_stream_id_t stream_id
,
256 uint8
* input_samples
,
257 uint8
* output_samples
,
259 const timespec
* input_ts
,
260 const timespec
* output_ts
,
262 CrasUnifiedStream
* me
= static_cast<CrasUnifiedStream
*>(arg
);
263 return me
->DispatchCallback(frames
,
270 // Static callback for stream errors.
271 int CrasUnifiedStream::StreamError(cras_client
* client
,
272 cras_stream_id_t stream_id
,
275 CrasUnifiedStream
* me
= static_cast<CrasUnifiedStream
*>(arg
);
276 me
->NotifyStreamError(err
);
280 // Calls the appropriate rendering function for this type of stream.
281 uint32
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.";
299 uint32
CrasUnifiedStream::WriteAudio(size_t frames
,
301 const timespec
* sample_ts
) {
302 DCHECK_EQ(frames
, static_cast<size_t>(output_bus_
->frames()));
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
);
308 int frames_filled
= source_callback_
->OnMoreData(
309 output_bus_
.get(), GetBytesLatency(latency_ts
));
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
);
316 return frames_filled
;
319 void CrasUnifiedStream::NotifyStreamError(int err
) {
320 // This will remove the stream from the client.
321 if (source_callback_
)
322 source_callback_
->OnError(this);