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

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: rebase before trying cq Created 8 years, 5 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
« no previous file with comments | « media/audio/linux/cras_input.h ('k') | media/audio/linux/cras_input_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 <math.h>
8
9 #include "base/basictypes.h"
10 #include "base/bind.h"
11 #include "base/logging.h"
12 #include "base/time.h"
13 #include "media/audio/audio_manager.h"
14 #include "media/audio/linux/alsa_util.h"
15 #include "media/audio/linux/audio_manager_linux.h"
16
17 namespace media {
18
19 CrasInputStream::CrasInputStream(const AudioParameters& params,
20 AudioManagerLinux* manager)
21 : audio_manager_(manager),
22 bytes_per_frame_(0),
23 callback_(NULL),
24 client_(NULL),
25 params_(params),
26 started_(false),
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 snd_pcm_format_t pcm_format =
54 alsa_util::BitsToFormat(params_.bits_per_sample());
55 if (pcm_format == SND_PCM_FORMAT_UNKNOWN) {
56 DLOG(WARNING) << "Unsupported bits/sample: " << params_.bits_per_sample();
57 return false;
58 }
59
60 // Create the client and connect to the CRAS server.
61 if (cras_client_create(&client_) < 0) {
62 DLOG(WARNING) << "Couldn't create CRAS client.\n";
63 client_ = NULL;
64 return false;
65 }
66
67 if (cras_client_connect(client_)) {
68 DLOG(WARNING) << "Couldn't connect CRAS client.\n";
69 cras_client_destroy(client_);
70 client_ = NULL;
71 return false;
72 }
73
74 // Then start running the client.
75 if (cras_client_run_thread(client_)) {
76 DLOG(WARNING) << "Couldn't run CRAS client.\n";
77 cras_client_destroy(client_);
78 client_ = NULL;
79 return false;
80 }
81
82 return true;
83 }
84
85 void CrasInputStream::Close() {
86 if (client_) {
87 cras_client_stop(client_);
88 cras_client_destroy(client_);
89 client_ = NULL;
90 }
91
92 if (callback_) {
93 callback_->OnClose(this);
94 callback_ = NULL;
95 }
96
97 // Signal to the manager that we're closed and can be removed.
98 // Should be last call in the method as it deletes "this".
99 audio_manager_->ReleaseInputStream(this);
100 }
101
102 void CrasInputStream::Start(AudioInputCallback* callback) {
103 DCHECK(client_);
104 DCHECK(callback);
105
106 // If already playing, stop before re-starting.
107 if (started_)
108 return;
109
110 callback_ = callback;
111
112 // Prepare |audio_format| and |stream_params| for the stream we
113 // will create.
114 cras_audio_format* audio_format = cras_audio_format_create(
115 alsa_util::BitsToFormat(params_.bits_per_sample()),
116 params_.sample_rate(),
117 params_.channels());
118 if (!audio_format) {
119 DLOG(WARNING) << "Error setting up audio parameters.";
120 callback_->OnError(this, -ENOMEM);
121 callback_ = NULL;
122 return;
123 }
124
125 unsigned int frames_per_packet = params_.frames_per_buffer();
126 cras_stream_params* stream_params = cras_client_stream_params_create(
127 CRAS_STREAM_INPUT,
128 frames_per_packet, // Total latency.
129 frames_per_packet, // Call back when this many ready.
130 frames_per_packet, // Minimum Callback level ignored for capture streams.
131 CRAS_STREAM_TYPE_DEFAULT,
132 0, // Unused flags.
133 this,
134 CrasInputStream::SamplesReady,
135 CrasInputStream::StreamError,
136 audio_format);
137 if (!stream_params) {
138 DLOG(WARNING) << "Error setting up stream parameters.";
139 callback_->OnError(this, -ENOMEM);
140 callback_ = NULL;
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.
150 if (cras_client_add_stream(client_, &stream_id_, stream_params) == 0) {
151 audio_manager_->IncreaseActiveInputStreamCount();
152 } else {
153 DLOG(WARNING) << "Failed to add the stream.";
154 callback_->OnError(this, -EIO);
155 callback_ = NULL;
156 }
157
158 // Done with config params.
159 cras_audio_format_destroy(audio_format);
160 cras_client_stream_params_destroy(stream_params);
161
162 started_ = true;
163 }
164
165 void CrasInputStream::Stop() {
166 DCHECK(client_);
167
168 if (!callback_ || !started_)
169 return;
170
171 // Removing the stream from the client stops audio.
172 cras_client_rm_stream(client_, stream_id_);
173
174 audio_manager_->DecreaseActiveInputStreamCount();
175
176 started_ = false;
177 }
178
179 // Static callback asking for samples. Run on high priority thread.
180 int CrasInputStream::SamplesReady(cras_client* client,
181 cras_stream_id_t stream_id,
182 uint8* samples,
183 size_t frames,
184 const timespec* sample_ts,
185 void* arg) {
186 CrasInputStream* me = static_cast<CrasInputStream*>(arg);
187 me->ReadAudio(frames, samples, sample_ts);
188 return frames;
189 }
190
191 // Static callback for stream errors.
192 int CrasInputStream::StreamError(cras_client* client,
193 cras_stream_id_t stream_id,
194 int err,
195 void* arg) {
196 CrasInputStream* me = static_cast<CrasInputStream*>(arg);
197 me->NotifyStreamError(err);
198 return 0;
199 }
200
201 void CrasInputStream::ReadAudio(size_t frames,
202 uint8* buffer,
203 const timespec* sample_ts) {
204 DCHECK(callback_);
205
206 timespec latency_ts = {0, 0};
207
208 // Determine latency and pass that on to the sink. sample_ts is the wall time
209 // indicating when the first sample in the buffer was captured. Convert that
210 // to latency in bytes.
211 cras_client_calc_capture_latency(sample_ts, &latency_ts);
212 double latency_usec =
213 latency_ts.tv_sec * base::Time::kMicrosecondsPerSecond +
214 latency_ts.tv_nsec / base::Time::kNanosecondsPerMicrosecond;
215 double frames_latency =
216 latency_usec * params_.sample_rate() / base::Time::kMicrosecondsPerSecond;
217 unsigned int bytes_latency =
218 static_cast<unsigned int>(frames_latency * bytes_per_frame_);
219
220 // Update the AGC volume level once every second. Note that, |volume| is
221 // also updated each time SetVolume() is called through IPC by the
222 // render-side AGC.
223 double normalized_volume = 0.0;
224 QueryAgcVolume(&normalized_volume);
225
226 callback_->OnData(this,
227 buffer,
228 frames * bytes_per_frame_,
229 bytes_latency,
230 normalized_volume);
231 }
232
233 void CrasInputStream::NotifyStreamError(int err) {
234 if (callback_)
235 callback_->OnError(this, err);
236 }
237
238 double CrasInputStream::GetMaxVolume() {
239 DCHECK(client_);
240
241 // Capture gain is returned as dB * 100 (150 => 1.5dBFS). Convert the dB
242 // value to a ratio before returning.
243 double dB = cras_client_get_system_max_capture_gain(client_) / 100.0;
244 return GetVolumeRatioFromDecibels(dB);
245 }
246
247 void CrasInputStream::SetVolume(double volume) {
248 DCHECK(client_);
249
250 // Convert from the passed volume ratio, to dB * 100.
251 double dB = GetDecibelsFromVolumeRatio(volume);
252 cras_client_set_system_capture_gain(client_, static_cast<long>(dB * 100.0));
253
254 // Update the AGC volume level based on the last setting above. Note that,
255 // the volume-level resolution is not infinite and it is therefore not
256 // possible to assume that the volume provided as input parameter can be
257 // used directly. Instead, a new query to the audio hardware is required.
258 // This method does nothing if AGC is disabled.
259 UpdateAgcVolume();
260 }
261
262 double CrasInputStream::GetVolume() {
263 if (!client_)
264 return 0.0;
265
266 long dB = cras_client_get_system_capture_gain(client_) / 100.0;
267 return GetVolumeRatioFromDecibels(dB);
268 }
269
270 double CrasInputStream::GetVolumeRatioFromDecibels(double dB) const {
271 return pow(10, dB / 20.0);
272 }
273
274 double CrasInputStream::GetDecibelsFromVolumeRatio(double volume_ratio) const {
275 return 20 * log10(volume_ratio);
276 }
277
278 } // namespace media
OLDNEW
« no previous file with comments | « media/audio/linux/cras_input.h ('k') | media/audio/linux/cras_input_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698