Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / media / audio / cras / cras_unified.cc
blob1656624c2081e190a457793df17a8f357b30331c
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"
10 namespace media {
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 // |<----------------------------------|
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 // | |
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.
51 CrasUnifiedStream::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);
65 output_bus_ = AudioBus::Create(params);
68 CrasUnifiedStream::~CrasUnifiedStream() {
69 DCHECK(!is_playing_);
72 bool CrasUnifiedStream::Open() {
73 // Sanity check input values.
74 if (params_.sample_rate() <= 0) {
75 LOG(WARNING) << "Unsupported audio frequency.";
76 return false;
79 if (AudioManagerCras::BitsToFormat(params_.bits_per_sample()) ==
80 SND_PCM_FORMAT_UNKNOWN) {
81 LOG(WARNING) << "Unsupported pcm format";
82 return false;
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;
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;
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;
107 return true;
110 void CrasUnifiedStream::Close() {
111 if (client_) {
112 cras_client_stop(client_);
113 cras_client_destroy(client_);
114 client_ = NULL;
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) {
123 CHECK(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[] = {
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
141 source_callback_ = callback;
143 // Only start if we can enter the playing state.
144 if (is_playing_)
145 return;
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;
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);
172 return;
175 cras_stream_params* stream_params = cras_client_unified_params_create(
176 stream_direction_,
177 params_.frames_per_buffer(),
178 CRAS_STREAM_TYPE_DEFAULT,
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;
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);
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;
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);
211 is_playing_ = true;
214 void CrasUnifiedStream::Stop() {
215 if (!client_)
216 return;
218 // Removing the stream from the client stops audio.
219 cras_client_rm_stream(client_, stream_id_);
221 is_playing_ = false;
224 void 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_);
231 void CrasUnifiedStream::GetVolume(double* volume) {
232 *volume = volume_;
235 uint32 CrasUnifiedStream::GetBytesLatency(
236 const struct timespec& latency_ts) {
237 uint32 latency_usec;
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;
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,
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);
270 // Static callback for stream errors.
271 int 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;
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.";
291 return 0;
292 default:
293 break;
296 return 0;
299 uint32 CrasUnifiedStream::WriteAudio(size_t frames,
300 uint8* buffer,
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);
325 } // namespace media