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

Side by Side Diff: media/audio/linux/cras_input.cc

Issue 10592014: media/audio/linux: Add CrasInputStream. (Closed) Base URL: http://git.chromium.org/chromium/src.git@master
Patch Set: Now better after input from xians. Created 8 years, 6 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_input.h"
6
7 #include "base/basictypes.h"
8 #include "base/bind.h"
9 #include "base/logging.h"
10 #include "base/time.h"
11 #include "media/audio/audio_manager.h"
12 #include "media/audio/linux/alsa_util.h"
13 #include "media/audio/linux/audio_manager_linux.h"
14
15 namespace media {
16
17 CrasInputStream::CrasInputStream(const AudioParameters& params,
18 AudioManagerLinux* manager)
19 : audio_manager_(manager),
20 bytes_per_frame_(0),
21 callback_(NULL),
22 client_(NULL),
23 sample_rate_(params.sample_rate()),
24 num_channels_(params.channels()),
scherkus (not reviewing) 2012/06/21 21:39:46 if you save params_ do you need to save sample_rat
dgreid 2012/06/22 05:36:32 Nice, I don't really need these two or pcm_format_
25 params_(params),
26 pcm_format_(alsa_util::BitsToFormat(params.bits_per_sample())),
27 stream_id_(0) {
28 DCHECK(audio_manager_);
29 }
30
31 CrasInputStream::~CrasInputStream() {
32 DCHECK(!client_);
33 }
34
35 bool CrasInputStream::Open() {
36 if (client_) {
37 NOTREACHED() << "CrasInputStream already open";
38 return false; // Already open.
39 }
40
41 // Sanity check input values.
42 if (params_.sample_rate() <= 0) {
43 DLOG(WARNING) << "Unsupported audio frequency.";
44 return false;
45 }
46
47 if (AudioParameters::AUDIO_PCM_LINEAR != params_.format() &&
48 AudioParameters::AUDIO_PCM_LOW_LATENCY != params_.format()) {
49 DLOG(WARNING) << "Unsupported audio format.";
50 return false;
51 }
52
53 if (pcm_format_ == SND_PCM_FORMAT_UNKNOWN) {
54 DLOG(WARNING) << "Unsupported bits/sample: " << params_.bits_per_sample();
55 return false;
56 }
57
58 // Create the client and connect to the CRAS server.
59 if (cras_client_create(&client_) < 0) {
60 DLOG(WARNING) << "Couldn't create CRAS client.\n";
61 client_ = NULL;
62 return false;
63 }
64
65 if (cras_client_connect(client_)) {
66 DLOG(WARNING) << "Couldn't connect CRAS client.\n";
67 cras_client_destroy(client_);
68 client_ = NULL;
69 return false;
70 }
71
72 // Then start running the client.
73 if (cras_client_run_thread(client_)) {
74 DLOG(WARNING) << "Couldn't run CRAS client.\n";
75 cras_client_destroy(client_);
76 client_ = NULL;
77 return false;
78 }
79
80 return true;
81 }
82
83 void CrasInputStream::Close() {
84 if (client_) {
85 cras_client_stop(client_);
86 cras_client_destroy(client_);
87 client_ = NULL;
88 }
89
90 if (callback_)
91 callback_->OnClose(this);
92
93 // Signal to the manager that we're closed and can be removed.
94 // Should be last call in the method as it deletes "this".
95 audio_manager_->ReleaseInputStream(this);
96 }
97
98 void CrasInputStream::Start(AudioInputCallback* callback) {
99 DCHECK(client_);
100 DCHECK(!callback_ && callback);
scherkus (not reviewing) 2012/06/21 21:39:46 split these DCHECKs into two so if they fail we kn
dgreid 2012/06/22 05:36:32 Done.
101 callback_ = callback;
102
103 // Prepare |audio_format| and |stream_params| for the stream we
104 // will create.
105 cras_audio_format* audio_format = cras_audio_format_create(
106 pcm_format_,
107 sample_rate_,
108 num_channels_);
109 if (!audio_format) {
110 DLOG(WARNING) << "Error setting up audio parameters.";
111 callback_->OnError(this, -ENOMEM);
112 callback_ = NULL;
113 return;
114 }
115
116 unsigned int frames_per_packet = params_.frames_per_buffer();
117 cras_stream_params* stream_params = cras_client_stream_params_create(
118 CRAS_STREAM_INPUT,
119 frames_per_packet, // Total latency.
120 frames_per_packet, // Call back when this many ready.
121 frames_per_packet, // Minimum Callback level ignored for capture streams.
122 CRAS_STREAM_TYPE_DEFAULT,
123 0, // Unused flags.
124 this,
125 CrasInputStream::SamplesReady,
126 CrasInputStream::StreamError,
127 audio_format);
128 if (!stream_params) {
129 DLOG(WARNING) << "Error setting up stream parameters.";
130 callback_->OnError(this, -ENOMEM);
131 callback_ = NULL;
132 cras_audio_format_destroy(audio_format);
133 return;
134 }
135
136 // Before starting the stream, save the number of bytes in a frame for use in
137 // the callback.
138 bytes_per_frame_ = cras_client_format_bytes_per_frame(audio_format);
139
140 // Adding the stream will start the audio callbacks.
141 if (cras_client_add_stream(client_, &stream_id_, stream_params) == 0) {
142 audio_manager_->IncreaseActiveInputStreamCount();
143 } else {
144 DLOG(WARNING) << "Failed to add the stream.";
145 callback_->OnError(this, -EIO);
146 callback_ = NULL;
147 }
148
149 // Done with config params.
150 cras_audio_format_destroy(audio_format);
151 cras_client_stream_params_destroy(stream_params);
152 }
153
154 void CrasInputStream::Stop() {
155 if (!client_ || !callback_)
scherkus (not reviewing) 2012/06/21 21:39:46 is this a DCHECK/programmer error?
dgreid 2012/06/22 05:36:32 Half and half, changed client_ to DCHECK. I left
156 return;
157
158 // Removing the stream from the client stops audio.
159 cras_client_rm_stream(client_, stream_id_);
160
161 audio_manager_->DecreaseActiveInputStreamCount();
162 }
163
164 // Static callback asking for samples. Run on high priority thread.
165 int CrasInputStream::SamplesReady(cras_client* client,
166 cras_stream_id_t stream_id,
167 uint8* samples,
168 size_t frames,
169 const timespec* sample_ts,
170 void* arg) {
171 CrasInputStream* me = static_cast<CrasInputStream*>(arg);
172 me->ReadAudio(frames, samples, sample_ts);
173 return frames;
174 }
175
176 // Static callback for stream errors.
177 int CrasInputStream::StreamError(cras_client* client,
178 cras_stream_id_t stream_id,
179 int err,
180 void* arg) {
181 CrasInputStream* me = static_cast<CrasInputStream*>(arg);
182 me->NotifyStreamError(err);
183 return 0;
184 }
185
186 void CrasInputStream::ReadAudio(size_t frames,
187 uint8* buffer,
188 const timespec* sample_ts) {
189 DCHECK(callback_);
190
191 timespec latency_ts = {0, 0};
scherkus (not reviewing) 2012/06/21 21:39:46 nit: remove extra space before =
dgreid 2012/06/22 05:36:32 Done.
192
193 // Determine latency and pass that on to the sink. sample_ts is the wall time
194 // indicating when the first sample in the buffer was captured. Convert that
195 // to latency in bytes.
196 cras_client_calc_capture_latency(sample_ts, &latency_ts);
197 double latency_usec = (latency_ts.tv_sec * 1e6) + latency_ts.tv_nsec * 1e-3;
scherkus (not reviewing) 2012/06/21 21:39:46 magic numbers! you can use base/time.h for kMicro
dgreid 2012/06/22 05:36:32 Done.
198 double frames_latency = latency_usec * sample_rate_ * 1e-6;
199 unsigned int bytes_latency =
200 static_cast<unsigned int>(frames_latency * bytes_per_frame_);
201
202 // Update the AGC volume level once every second. Note that, |volume| is
203 // also updated each time SetVolume() is called through IPC by the
204 // render-side AGC.
205 double normalized_volume = 0.0;
206 QueryAgcVolume(&normalized_volume);
207
208 callback_->OnData(this,
209 buffer,
210 frames * bytes_per_frame_,
211 bytes_latency,
212 normalized_volume);
213 }
214
215 void CrasInputStream::NotifyStreamError(int err) {
216 if (callback_)
217 callback_->OnError(this, err);
218 }
219
220 double CrasInputStream::GetMaxVolume() {
221 DCHECK(client_);
222
223 // Return the max volume as a double to match what the alsa input does.
224 return static_cast<double>(cras_client_get_system_max_capture_gain(client_));
scherkus (not reviewing) 2012/06/21 21:39:46 ditto here -- see my concerns about casting below
dgreid 2012/06/22 05:36:32 it's weird, see below.
225 }
226
227 void CrasInputStream::SetVolume(double volume) {
228 DCHECK(client_);
229
230 cras_client_set_system_capture_gain(client_, static_cast<long>(volume));
231
232 // Update the AGC volume level based on the last setting above. Note that,
233 // the volume-level resolution is not infinite and it is therefore not
234 // possible to assume that the volume provided as input parameter can be
235 // used directly. Instead, a new query to the audio hardware is required.
236 // This method does nothing if AGC is disabled.
237 UpdateAgcVolume();
238 }
239
240 double CrasInputStream::GetVolume() {
241 if (!client_)
scherkus (not reviewing) 2012/06/21 21:39:46 SetVolume() when client_ == NULL is DCHECK() but g
dgreid 2012/06/22 05:36:32 Not that I can find. The only path to get here th
242 return 0.0;
243
244 long current_volume = cras_client_get_system_capture_gain(client_);
245
246 // Only want the gain, limit reported level to zero.
247 if (current_volume < 0)
248 return 0.0;
249
250 return static_cast<double>(current_volume);
scherkus (not reviewing) 2012/06/21 21:39:46 what's the range of value for current_volume? doe
dgreid 2012/06/22 05:36:32 I thought this was really strange too, but went wi
251 }
252
253 } // namespace media
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698