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

Side by Side Diff: media/audio/linux/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: Created 7 years, 11 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/linux/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/linux/alsa_util.h"
12 #include "media/audio/linux/audio_manager_linux.h"
13
14 namespace media {
15
16 // TODO(dgreid): support more than hard-coded stereo input.
17 // Ideally we would like to receive this value as a constructor argument.
18 // crbug.com/147326
19 static const int kDefaultInputChannels = 2;
20
21 // Overview of operation:
22 // 1) An object of CrasUnifiedStream is created by the AudioManager
23 // factory: audio_man->MakeAudioStream().
24 // 2) Next some thread will call Open(), at that point a client is created and
25 // configured for the correct format and sample rate.
26 // 3) Then Start(source) is called and a stream is added to the CRAS client
27 // which will create its own thread that periodically calls the source for more
28 // data as buffers are being consumed.
29 // 4) When finished Stop() is called, which is handled by stopping the stream.
30 // 5) Finally Close() is called. It cleans up and notifies the audio manager,
31 // which likely will destroy this object.
32
33 CrasUnifiedStream::CrasUnifiedStream(const AudioParameters& params,
34 AudioManagerLinux* manager)
35 : client_(NULL),
36 stream_id_(0),
37 samples_per_packet_(params.frames_per_buffer()),
38 bytes_per_frame_(0),
39 frame_rate_(params.sample_rate()),
40 num_channels_(params.channels()),
41 pcm_format_(alsa_util::BitsToFormat(params.bits_per_sample())),
42 is_playing_(false),
43 volume_(1.0),
44 manager_(manager),
45 source_callback_(NULL),
46 input_bus_(AudioBus::Create(kDefaultInputChannels,
47 params.frames_per_buffer())),
48 output_bus_(AudioBus::Create(params)) {
49 // We must have a manager.
50 DCHECK(manager_);
51 }
52
53 CrasUnifiedStream::~CrasUnifiedStream() {
54 DCHECK(!is_playing_);
55 }
56
57 bool CrasUnifiedStream::Open() {
58 // Sanity check input values.
59 if (frame_rate_ <= 0) {
60 LOG(WARNING) << "Unsupported audio frequency.";
61 return false;
62 }
63
64 if (pcm_format_ == SND_PCM_FORMAT_UNKNOWN) {
65 LOG(WARNING) << "Unsupported pcm format";
66 return false;
67 }
68
69 // Create the client and connect to the CRAS server.
70 int err = cras_client_create(&client_);
71 if (err < 0) {
72 LOG(WARNING) << "Couldn't create CRAS client.\n";
73 client_ = NULL;
74 return false;
75 }
76
77 err = cras_client_connect(client_);
78 if (err) {
79 LOG(WARNING) << "Couldn't connect CRAS client.\n";
80 cras_client_destroy(client_);
81 client_ = NULL;
82 return false;
83 }
84
85 // Then start running the client.
86 err = cras_client_run_thread(client_);
87 if (err) {
88 LOG(WARNING) << "Couldn't run CRAS client.\n";
89 cras_client_destroy(client_);
90 client_ = NULL;
91 return false;
92 }
93
94 return true;
95 }
96
97 void CrasUnifiedStream::Close() {
98 if (client_) {
99 cras_client_stop(client_);
100 cras_client_destroy(client_);
101 client_ = NULL;
102 }
103
104 // Signal to the manager that we're closed and can be removed.
105 // Should be last call in the method as it deletes "this".
106 manager_->ReleaseOutputStream(this);
107 }
108
109 void CrasUnifiedStream::Start(AudioSourceCallback* callback) {
110 CHECK(callback);
111 source_callback_ = callback;
112
113 // Only start if we can enter the playing state.
114 if (is_playing_)
115 return;
116
117 // Prepare |audio_format| and |stream_params| for the stream we
118 // will create.
119 cras_audio_format* audio_format = cras_audio_format_create(
120 pcm_format_,
121 frame_rate_,
122 num_channels_);
123 if (audio_format == NULL) {
124 LOG(WARNING) << "Error setting up audio parameters.";
125 callback->OnError(this, -ENOMEM);
126 return;
127 }
128
129 cras_stream_params* stream_params = cras_client_unified_params_create(
130 CRAS_STREAM_UNIFIED,
131 samples_per_packet_,
132 CRAS_STREAM_TYPE_DEFAULT,
133 0,
134 this,
135 CrasUnifiedStream::UnifiedCallback,
136 CrasUnifiedStream::StreamError,
137 audio_format);
138 if (stream_params == NULL) {
139 LOG(WARNING) << "Error setting up stream parameters.";
140 callback->OnError(this, -ENOMEM);
141 cras_audio_format_destroy(audio_format);
142 return;
143 }
144
145 // Before starting the stream, save the number of bytes in a frame for use in
146 // the callback.
147 bytes_per_frame_ = cras_client_format_bytes_per_frame(audio_format);
148
149 // Adding the stream will start the audio callbacks requesting data.
150 int err = cras_client_add_stream(client_, &stream_id_, stream_params);
151 if (err < 0) {
152 LOG(WARNING) << "Failed to add the stream";
153 callback->OnError(this, err);
154 cras_audio_format_destroy(audio_format);
155 cras_client_stream_params_destroy(stream_params);
156 return;
157 }
158
159 // Set initial volume.
160 cras_client_set_stream_volume(client_, stream_id_, volume_);
161
162 // Done with config params.
163 cras_audio_format_destroy(audio_format);
164 cras_client_stream_params_destroy(stream_params);
165
166 is_playing_ = true;
167 }
168
169 void CrasUnifiedStream::Stop() {
170 if (!client_)
171 return;
172
173 // Removing the stream from the client stops audio.
174 cras_client_rm_stream(client_, stream_id_);
175
176 is_playing_ = false;
177 }
178
179 void CrasUnifiedStream::SetVolume(double volume) {
180 if (!client_)
181 return;
182 volume_ = static_cast<float>(volume);
183 cras_client_set_stream_volume(client_, stream_id_, volume_);
184 }
185
186 void CrasUnifiedStream::GetVolume(double* volume) {
187 *volume = volume_;
188 }
189
190 // Static callback asking for samples.
191 int CrasUnifiedStream::UnifiedCallback(cras_client* client,
192 cras_stream_id_t stream_id,
193 uint8* input_samples,
194 uint8* output_samples,
195 unsigned int frames,
196 const timespec* input_ts,
197 const timespec* output_ts,
198 void* arg) {
199 CrasUnifiedStream* me = static_cast<CrasUnifiedStream*>(arg);
200 return me->Render(frames, input_samples, output_samples, input_ts, output_ts);
201 }
202
203 // Static callback for stream errors.
204 int CrasUnifiedStream::StreamError(cras_client* client,
205 cras_stream_id_t stream_id,
206 int err,
207 void* arg) {
208 CrasUnifiedStream* me = static_cast<CrasUnifiedStream*>(arg);
209 me->NotifyStreamError(err);
210 return 0;
211 }
212
213 // Note this is run from a real time thread, so don't waste cycles here.
214 uint32 CrasUnifiedStream::Render(size_t frames,
215 uint8* input_samples,
216 uint8* output_samples,
217 const timespec* input_ts,
218 const timespec* output_ts) {
219 unsigned int bytes_per_sample = bytes_per_frame_ / num_channels_;
220
221 DCHECK_EQ(frames, static_cast<size_t>(output_bus_->frames()));
222
223 input_bus_->FromInterleaved(input_samples, frames, bytes_per_sample);
224
225 int frames_filled = source_callback_->OnMoreIOData(
226 input_bus_.get(), output_bus_.get(), AudioBuffersState(0, 0));
227
228 output_bus_->ToInterleaved(frames_filled, bytes_per_sample, output_samples);
229
230 return frames_filled;
231 }
232
233 void CrasUnifiedStream::NotifyStreamError(int err) {
234 // This will remove the stream from the client.
235 if (source_callback_)
236 source_callback_->OnError(this, err);
237 }
238
239 } // namespace media
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698