Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(253)

Side by Side Diff: media/audio/cras/cras_unified.cc

Issue 11959018: Add a unified audio I/O backend for ChromeOS. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: DCHECKs moved copyright updated. Created 7 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
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 <cras_client.h>
8
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"
14
15 namespace media {
16
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.
28 //
29 // For output-only streams, a unified stream is created with 0 input channels.
30 //
31 // Simplified data flow for unified streams:
32 //
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 // | |
52 //
53 // Simplified data flow for output only streams:
54 //
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 // | |
74 //
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.
79
80 CrasUnifiedStream::CrasUnifiedStream(const AudioParameters& params,
81 AudioManagerCras* manager)
82 : client_(NULL),
83 stream_id_(0),
84 samples_per_packet_(params.frames_per_buffer()),
DaleCurtis 2013/03/26 19:23:01 Instead of storing these four variables, you could
85 bytes_per_frame_(0),
86 frame_rate_(params.sample_rate()),
87 output_channels_(params.channels()),
88 pcm_format_(alsa_util::BitsToFormat(params.bits_per_sample())),
89 is_playing_(false),
90 volume_(1.0),
91 manager_(manager),
92 source_callback_(NULL),
93 input_bus_(NULL),
94 output_bus_(NULL) {
95 DCHECK(manager_);
96 DCHECK(output_channels_ > 0);
97
98 // Must have at least one input or output. If there are both they must be the
99 // same.
100 unsigned int input_channels = params.input_channels();
DaleCurtis 2013/03/26 19:23:01 Just leave this as int for now. Long term security
101
102 if (input_channels) {
103 // A unified stream for input and output.
104 DCHECK(output_channels_ == input_channels);
105 stream_direction_ = CRAS_STREAM_UNIFIED;
106 input_bus_ = AudioBus::Create(input_channels,
107 params.frames_per_buffer());
108 }
109
110 output_bus_ = AudioBus::Create(params);
111 }
112
113 CrasUnifiedStream::~CrasUnifiedStream() {
114 DCHECK(!is_playing_);
115 }
116
117 bool CrasUnifiedStream::Open() {
118 // Sanity check input values.
119 if (frame_rate_ <= 0) {
120 LOG(WARNING) << "Unsupported audio frequency.";
121 return false;
122 }
123
124 if (pcm_format_ == SND_PCM_FORMAT_UNKNOWN) {
125 LOG(WARNING) << "Unsupported pcm format";
126 return false;
127 }
128
129 // Create the client and connect to the CRAS server.
130 if (cras_client_create(&client_)) {
131 LOG(WARNING) << "Couldn't create CRAS client.\n";
132 client_ = NULL;
133 return false;
134 }
135
136 if (cras_client_connect(client_)) {
137 LOG(WARNING) << "Couldn't connect CRAS client.\n";
138 cras_client_destroy(client_);
139 client_ = NULL;
140 return false;
141 }
142
143 // Then start running the client.
144 if (cras_client_run_thread(client_)) {
145 LOG(WARNING) << "Couldn't run CRAS client.\n";
146 cras_client_destroy(client_);
147 client_ = NULL;
148 return false;
149 }
150
151 return true;
152 }
153
154 void CrasUnifiedStream::Close() {
155 if (client_) {
156 cras_client_stop(client_);
157 cras_client_destroy(client_);
158 client_ = NULL;
159 }
160
161 // Signal to the manager that we're closed and can be removed.
162 // Should be last call in the method as it deletes "this".
163 manager_->ReleaseOutputStream(this);
164 }
165
166 void CrasUnifiedStream::Start(AudioSourceCallback* callback) {
167 CHECK(callback);
168 source_callback_ = callback;
169
170 // Only start if we can enter the playing state.
171 if (is_playing_)
172 return;
173
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 pcm_format_,
178 frame_rate_,
179 output_channels_);
180 if (audio_format == NULL) {
DaleCurtis 2013/03/26 19:23:01 if !audio_format?
181 LOG(WARNING) << "Error setting up audio parameters.";
182 callback->OnError(this, -ENOMEM);
183 return;
184 }
185
186 cras_stream_params* stream_params = cras_client_unified_params_create(
187 stream_direction_,
188 samples_per_packet_,
189 CRAS_STREAM_TYPE_DEFAULT,
190 0,
191 this,
192 CrasUnifiedStream::UnifiedCallback,
193 CrasUnifiedStream::StreamError,
194 audio_format);
195 if (stream_params == NULL) {
DaleCurtis 2013/03/26 19:23:01 Ditto.
196 LOG(WARNING) << "Error setting up stream parameters.";
197 callback->OnError(this, -ENOMEM);
198 cras_audio_format_destroy(audio_format);
199 return;
200 }
201
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);
205
206 // Adding the stream will start the audio callbacks requesting data.
207 int err = cras_client_add_stream(client_, &stream_id_, stream_params);
208 if (err < 0) {
209 LOG(WARNING) << "Failed to add the stream";
210 callback->OnError(this, err);
DaleCurtis 2013/03/26 19:23:01 Interface has changed to remove err.
211 cras_audio_format_destroy(audio_format);
212 cras_client_stream_params_destroy(stream_params);
213 return;
214 }
215
216 // Set initial volume.
217 cras_client_set_stream_volume(client_, stream_id_, volume_);
218
219 // Done with config params.
220 cras_audio_format_destroy(audio_format);
221 cras_client_stream_params_destroy(stream_params);
222
223 is_playing_ = true;
224 }
225
226 void CrasUnifiedStream::Stop() {
227 if (!client_)
228 return;
229
230 // Removing the stream from the client stops audio.
231 cras_client_rm_stream(client_, stream_id_);
232
233 is_playing_ = false;
234 }
235
236 void CrasUnifiedStream::SetVolume(double volume) {
237 if (!client_)
238 return;
239 volume_ = static_cast<float>(volume);
240 cras_client_set_stream_volume(client_, stream_id_, volume_);
241 }
242
243 void CrasUnifiedStream::GetVolume(double* volume) {
244 *volume = volume_;
245 }
246
247 unsigned int CrasUnifiedStream::GetBytesLatency(
DaleCurtis 2013/03/26 19:23:01 If unsigned types are necessary, using size_t inst
248 const struct timespec& latency_ts) {
249 uint32 latency_usec;
250
251 // Treat negative latency (if we are too slow to render) as 0.
252 if (latency_ts.tv_sec < 0 || latency_ts.tv_nsec < 0) {
253 latency_usec = 0;
254 } else {
255 latency_usec = (latency_ts.tv_sec * base::Time::kMicrosecondsPerSecond) +
256 latency_ts.tv_nsec / base::Time::kNanosecondsPerMicrosecond;
257 }
258
259 double frames_latency =
260 latency_usec * frame_rate_ / base::Time::kMicrosecondsPerSecond;
261
262 return static_cast<unsigned int>(frames_latency * bytes_per_frame_);
263 }
264
265 // Static callback asking for samples.
266 int CrasUnifiedStream::UnifiedCallback(cras_client* client,
267 cras_stream_id_t stream_id,
268 uint8* input_samples,
269 uint8* output_samples,
270 unsigned int frames,
271 const timespec* input_ts,
272 const timespec* output_ts,
273 void* arg) {
274 CrasUnifiedStream* me = static_cast<CrasUnifiedStream*>(arg);
275 return me->DispatchCalback(frames,
276 input_samples,
277 output_samples,
278 input_ts,
279 output_ts);
280 }
281
282 // Static callback for stream errors.
283 int CrasUnifiedStream::StreamError(cras_client* client,
284 cras_stream_id_t stream_id,
285 int err,
286 void* arg) {
287 CrasUnifiedStream* me = static_cast<CrasUnifiedStream*>(arg);
288 me->NotifyStreamError(err);
289 return 0;
290 }
291
292 // Calls the appropriate rendering function for this type of stream.
293 uint32 CrasUnifiedStream::DispatchCalback(size_t frames,
294 uint8* input_samples,
295 uint8* output_samples,
296 const timespec* input_ts,
297 const timespec* output_ts) {
298 switch (stream_direction_) {
299 case CRAS_STREAM_OUTPUT:
300 return WriteAudio(frames, output_samples, output_ts);
301 case CRAS_STREAM_INPUT:
302 NOTREACHED() << "CrasUnifiedStream doesn't support input streams.";
303 return 0;
304 case CRAS_STREAM_UNIFIED:
305 return ReadWriteAudio(frames, input_samples, output_samples,
306 input_ts, output_ts);
307 }
308
309 return 0;
310 }
311
312 // Note these are run from a real time thread, so don't waste cycles here.
313 uint32 CrasUnifiedStream::ReadWriteAudio(size_t frames,
314 uint8* input_samples,
315 uint8* output_samples,
316 const timespec* input_ts,
317 const timespec* output_ts) {
318 DCHECK_EQ(frames, static_cast<size_t>(output_bus_->frames()));
319 DCHECK(source_callback_);
320
321 unsigned int bytes_per_sample = bytes_per_frame_ / output_channels_;
322 input_bus_->FromInterleaved(input_samples, frames, bytes_per_sample);
323
324 // Determine latency and pass that on to the source. We have the capture time
325 // of the first input sample and the playback time of the next audio sample
326 // passed from the audio server, add them together for total latency.
327 unsigned int total_delay_bytes;
328 timespec latency_ts = {0, 0};
329 cras_client_calc_capture_latency(input_ts, &latency_ts);
330 total_delay_bytes = GetBytesLatency(latency_ts);
331 cras_client_calc_playback_latency(output_ts, &latency_ts);
332 total_delay_bytes += GetBytesLatency(latency_ts);
333
334 int frames_filled = source_callback_->OnMoreIOData(
335 input_bus_.get(),
336 output_bus_.get(),
337 AudioBuffersState(0, total_delay_bytes));
338
339 output_bus_->ToInterleaved(frames_filled, bytes_per_sample, output_samples);
340
341 return frames_filled;
342 }
343
344 uint32 CrasUnifiedStream::WriteAudio(size_t frames,
345 uint8* buffer,
346 const timespec* sample_ts) {
347 DCHECK_EQ(frames, static_cast<size_t>(output_bus_->frames()));
348
349 // Determine latency and pass that on to the source.
350 timespec latency_ts = {0, 0};
351 cras_client_calc_playback_latency(sample_ts, &latency_ts);
352
353 int frames_filled = source_callback_->OnMoreData(
354 output_bus_.get(), AudioBuffersState(0, GetBytesLatency(latency_ts)));
355
356 // Note: If this ever changes to output raw float the data must be clipped and
357 // sanitized since it may come from an untrusted source such as NaCl.
358 output_bus_->ToInterleaved(
359 frames_filled, bytes_per_frame_ / output_channels_, buffer);
360
361 return frames_filled;
362 }
363
364 void CrasUnifiedStream::NotifyStreamError(int err) {
365 // This will remove the stream from the client.
366 if (source_callback_)
367 source_callback_->OnError(this, err);
368 }
369
370 } // namespace media
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698