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

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: Rebase and remove redundant code. Created 7 years, 10 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 (c) 2012 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/logging.h"
10 #include "media/audio/audio_util.h"
11 #include "media/audio/cras/audio_manager_cras.h"
12 #include "media/audio/linux/alsa_util.h"
13
14 namespace media {
15
16 // Overview of operation:
17 // 1) An object of CrasUnifiedStream is created by the AudioManager
18 // factory: audio_man->MakeAudioStream().
19 // 2) Next some thread will call Open(), at that point a client is created and
20 // configured for the correct format and sample rate.
21 // 3) Then Start(source) is called and a stream is added to the CRAS client
22 // which will create its own thread that periodically calls the source for more
23 // data as buffers are being consumed.
24 // 4) When finished Stop() is called, which is handled by stopping the stream.
25 // 5) Finally Close() is called. It cleans up and notifies the audio manager,
26 // which likely will destroy this object.
27
28 CrasUnifiedStream::CrasUnifiedStream(const AudioParameters& params,
29 AudioManagerCras* manager)
30 : client_(NULL),
31 stream_id_(0),
32 samples_per_packet_(params.frames_per_buffer()),
33 bytes_per_frame_(0),
34 frame_rate_(params.sample_rate()),
35 output_channels_(params.channels()),
36 pcm_format_(alsa_util::BitsToFormat(params.bits_per_sample())),
37 is_playing_(false),
38 volume_(1.0),
39 manager_(manager),
40 source_callback_(NULL),
41 input_bus_(NULL),
42 output_bus_(NULL) {
43 int input_channels = params.input_channels();
44
45 // We must have a manager.
46 DCHECK(manager_);
47 // Must have at least one input or output. If there are both they must be the
48 // same.
49 DCHECK(output_channels_ || input_channels);
50 if (output_channels_ && input_channels)
51 DCHECK(output_channels_ && input_channels);
52
53 if (input_channels)
54 input_bus_ = AudioBus::Create(input_channels,
55 params.frames_per_buffer());
56 if (output_channels_)
57 output_bus_ = AudioBus::Create(params);
58
59 if (output_channels_ && input_channels)
60 stream_direction_ = CRAS_STREAM_UNIFIED;
61 else if (output_channels_)
62 stream_direction_ = CRAS_STREAM_OUTPUT;
63 else
64 NOTREACHED() << "Input only streams not supported by CrasUnifiedStream.";
65 }
66
67 CrasUnifiedStream::~CrasUnifiedStream() {
68 DCHECK(!is_playing_);
69 }
70
71 bool CrasUnifiedStream::Open() {
72 // Sanity check input values.
73 if (frame_rate_ <= 0) {
74 LOG(WARNING) << "Unsupported audio frequency.";
75 return false;
76 }
77
78 if (pcm_format_ == SND_PCM_FORMAT_UNKNOWN) {
79 LOG(WARNING) << "Unsupported pcm format";
80 return false;
81 }
82
83 // Create the client and connect to the CRAS server.
84 int err = cras_client_create(&client_);
85 if (err < 0) {
86 LOG(WARNING) << "Couldn't create CRAS client.\n";
87 client_ = NULL;
88 return false;
89 }
90
91 err = cras_client_connect(client_);
92 if (err) {
93 LOG(WARNING) << "Couldn't connect CRAS client.\n";
94 cras_client_destroy(client_);
95 client_ = NULL;
96 return false;
97 }
98
99 // Then start running the client.
100 err = cras_client_run_thread(client_);
101 if (err) {
102 LOG(WARNING) << "Couldn't run CRAS client.\n";
103 cras_client_destroy(client_);
104 client_ = NULL;
105 return false;
106 }
107
108 return true;
109 }
110
111 void CrasUnifiedStream::Close() {
112 if (client_) {
113 cras_client_stop(client_);
114 cras_client_destroy(client_);
115 client_ = NULL;
116 }
117
118 // Signal to the manager that we're closed and can be removed.
119 // Should be last call in the method as it deletes "this".
120 manager_->ReleaseOutputStream(this);
121 }
122
123 void CrasUnifiedStream::Start(AudioSourceCallback* callback) {
124 CHECK(callback);
125 source_callback_ = callback;
126
127 // Only start if we can enter the playing state.
128 if (is_playing_)
129 return;
130
131 // Prepare |audio_format| and |stream_params| for the stream we
132 // will create.
133 cras_audio_format* audio_format = cras_audio_format_create(
134 pcm_format_,
135 frame_rate_,
136 output_channels_);
137 if (audio_format == NULL) {
138 LOG(WARNING) << "Error setting up audio parameters.";
139 callback->OnError(this, -ENOMEM);
140 return;
141 }
142
143 cras_stream_params* stream_params = cras_client_unified_params_create(
144 stream_direction_,
145 samples_per_packet_,
146 CRAS_STREAM_TYPE_DEFAULT,
147 0,
148 this,
149 CrasUnifiedStream::UnifiedCallback,
150 CrasUnifiedStream::StreamError,
151 audio_format);
152 if (stream_params == NULL) {
153 LOG(WARNING) << "Error setting up stream parameters.";
154 callback->OnError(this, -ENOMEM);
155 cras_audio_format_destroy(audio_format);
156 return;
157 }
158
159 // Before starting the stream, save the number of bytes in a frame for use in
160 // the callback.
161 bytes_per_frame_ = cras_client_format_bytes_per_frame(audio_format);
162
163 // Adding the stream will start the audio callbacks requesting data.
164 int err = cras_client_add_stream(client_, &stream_id_, stream_params);
165 if (err < 0) {
166 LOG(WARNING) << "Failed to add the stream";
167 callback->OnError(this, err);
168 cras_audio_format_destroy(audio_format);
169 cras_client_stream_params_destroy(stream_params);
170 return;
171 }
172
173 // Set initial volume.
174 cras_client_set_stream_volume(client_, stream_id_, volume_);
175
176 // Done with config params.
177 cras_audio_format_destroy(audio_format);
178 cras_client_stream_params_destroy(stream_params);
179
180 is_playing_ = true;
181 }
182
183 void CrasUnifiedStream::Stop() {
184 if (!client_)
185 return;
186
187 // Removing the stream from the client stops audio.
188 cras_client_rm_stream(client_, stream_id_);
189
190 is_playing_ = false;
191 }
192
193 void CrasUnifiedStream::SetVolume(double volume) {
194 if (!client_)
195 return;
196 volume_ = static_cast<float>(volume);
197 cras_client_set_stream_volume(client_, stream_id_, volume_);
198 }
199
200 void CrasUnifiedStream::GetVolume(double* volume) {
201 *volume = volume_;
202 }
203
204 unsigned int CrasUnifiedStream::GetBytesLatency(
205 const struct timespec* latency_ts) {
206 uint32 latency_usec;
207
208 // Treat negative latency (if we are too slow to render) as 0.
209 if (latency_ts->tv_sec < 0 || latency_ts->tv_nsec < 0)
210 latency_usec = 0;
211 else
212 latency_usec = (latency_ts->tv_sec * base::Time::kMicrosecondsPerSecond) +
213 latency_ts->tv_nsec / base::Time::kNanosecondsPerMicrosecond;
214
215 double frames_latency =
216 latency_usec * frame_rate_ / base::Time::kMicrosecondsPerSecond;
217
218 return static_cast<unsigned int>(frames_latency * bytes_per_frame_);
219 }
220
221 // Static callback asking for samples.
222 int CrasUnifiedStream::UnifiedCallback(cras_client* client,
223 cras_stream_id_t stream_id,
224 uint8* input_samples,
225 uint8* output_samples,
226 unsigned int frames,
227 const timespec* input_ts,
228 const timespec* output_ts,
229 void* arg) {
230 CrasUnifiedStream* me = static_cast<CrasUnifiedStream*>(arg);
231 return me->DispatchCalback(frames,
232 input_samples,
233 output_samples,
234 input_ts,
235 output_ts);
236 }
237
238 // Static callback for stream errors.
239 int CrasUnifiedStream::StreamError(cras_client* client,
240 cras_stream_id_t stream_id,
241 int err,
242 void* arg) {
243 CrasUnifiedStream* me = static_cast<CrasUnifiedStream*>(arg);
244 me->NotifyStreamError(err);
245 return 0;
246 }
247
248 // Calls the appropriate rendering funciton for this type of stream.
249 uint32 CrasUnifiedStream::DispatchCalback(size_t frames,
250 uint8* input_samples,
251 uint8* output_samples,
252 const timespec* input_ts,
253 const timespec* output_ts) {
254 switch (stream_direction_) {
255 case CRAS_STREAM_OUTPUT:
256 return WriteAudio(frames, output_samples, output_ts);
257 case CRAS_STREAM_INPUT:
258 NOTREACHED() << "CrasUnifiedStream doesn't support input streams.";
259 return 0;
260 case CRAS_STREAM_UNIFIED:
261 return ReadWriteAudio(frames, input_samples, output_samples,
262 input_ts, output_ts);
263 }
264
265 return 0;
266 }
267
268 // Note these are run from a real time thread, so don't waste cycles here.
269 uint32 CrasUnifiedStream::ReadWriteAudio(size_t frames,
270 uint8* input_samples,
271 uint8* output_samples,
272 const timespec* input_ts,
273 const timespec* output_ts) {
274 unsigned int bytes_per_sample = bytes_per_frame_ / output_channels_;
275
276 DCHECK_EQ(frames, static_cast<size_t>(output_bus_->frames()));
277 DCHECK(source_callback_);
278
279 if (input_samples)
280 input_bus_->FromInterleaved(input_samples, frames, bytes_per_sample);
281
282 int frames_filled = source_callback_->OnMoreIOData(
283 input_bus_.get(), output_bus_.get(), AudioBuffersState(0, 0));
284
285 output_bus_->ToInterleaved(frames_filled, bytes_per_sample, output_samples);
286
287 return frames_filled;
288 }
289
290 uint32 CrasUnifiedStream::WriteAudio(size_t frames,
291 uint8* buffer,
292 const timespec* sample_ts) {
293 timespec latency_ts = {0, 0};
294
295 DCHECK_EQ(frames, static_cast<size_t>(output_bus_->frames()));
296
297 // Determine latency and pass that on to the source.
298 cras_client_calc_playback_latency(sample_ts, &latency_ts);
299
300 int frames_filled = source_callback_->OnMoreData(
301 output_bus_.get(), AudioBuffersState(0, GetBytesLatency(&latency_ts)));
302
303 // Note: If this ever changes to output raw float the data must be clipped and
304 // sanitized since it may come from an untrusted source such as NaCl.
305 output_bus_->ToInterleaved(
306 frames_filled, bytes_per_frame_ / output_channels_, buffer);
307
308 return frames_filled;
309 }
310
311 void CrasUnifiedStream::NotifyStreamError(int err) {
312 // This will remove the stream from the client.
313 if (source_callback_)
314 source_callback_->OnError(this, err);
315 }
316
317 } // namespace media
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698