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

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: allow repeated starts 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
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 params_(params),
24 stream_id_(0) {
25 DCHECK(audio_manager_);
26 }
27
28 CrasInputStream::~CrasInputStream() {
29 DCHECK(!client_);
30 }
31
32 bool CrasInputStream::Open() {
33 if (client_) {
34 NOTREACHED() << "CrasInputStream already open";
35 return false; // Already open.
36 }
37
38 // Sanity check input values.
39 if (params_.sample_rate() <= 0) {
40 DLOG(WARNING) << "Unsupported audio frequency.";
41 return false;
42 }
43
44 if (AudioParameters::AUDIO_PCM_LINEAR != params_.format() &&
45 AudioParameters::AUDIO_PCM_LOW_LATENCY != params_.format()) {
46 DLOG(WARNING) << "Unsupported audio format.";
47 return false;
48 }
49
50 snd_pcm_format_t pcm_format =
51 alsa_util::BitsToFormat(params_.bits_per_sample());
52 if (pcm_format == SND_PCM_FORMAT_UNKNOWN) {
53 DLOG(WARNING) << "Unsupported bits/sample: " << params_.bits_per_sample();
54 return false;
55 }
56
57 // Create the client and connect to the CRAS server.
58 if (cras_client_create(&client_) < 0) {
59 DLOG(WARNING) << "Couldn't create CRAS client.\n";
60 client_ = NULL;
61 return false;
62 }
63
64 if (cras_client_connect(client_)) {
65 DLOG(WARNING) << "Couldn't connect CRAS client.\n";
66 cras_client_destroy(client_);
67 client_ = NULL;
68 return false;
69 }
70
71 // Then start running the client.
72 if (cras_client_run_thread(client_)) {
73 DLOG(WARNING) << "Couldn't run CRAS client.\n";
74 cras_client_destroy(client_);
75 client_ = NULL;
76 return false;
77 }
78
79 return true;
80 }
81
82 void CrasInputStream::Close() {
83 if (client_) {
84 cras_client_stop(client_);
85 cras_client_destroy(client_);
86 client_ = NULL;
87 }
88
89 // Signal to the manager that we're closed and can be removed.
90 // Should be last call in the method as it deletes "this".
91 audio_manager_->ReleaseInputStream(this);
92 }
93
94 void CrasInputStream::Start(AudioInputCallback* callback) {
95 DCHECK(client_);
96 DCHECK(callback);
97
98 // If already playing, stop before re-starting.
99 if (callback_)
no longer working on chromium 2012/06/26 21:42:50 use an extra flag like started_ to protect calling
dgreid 2012/06/27 07:58:07 Done.
100 Stop();
101
102 callback_ = callback;
103
104 // Prepare |audio_format| and |stream_params| for the stream we
105 // will create.
106 cras_audio_format* audio_format = cras_audio_format_create(
107 alsa_util::BitsToFormat(params_.bits_per_sample()),
108 params_.sample_rate(),
109 params_.channels());
110 if (!audio_format) {
111 DLOG(WARNING) << "Error setting up audio parameters.";
112 callback_->OnError(this, -ENOMEM);
113 callback_ = NULL;
114 return;
115 }
116
117 unsigned int frames_per_packet = params_.frames_per_buffer();
118 cras_stream_params* stream_params = cras_client_stream_params_create(
119 CRAS_STREAM_INPUT,
120 frames_per_packet, // Total latency.
121 frames_per_packet, // Call back when this many ready.
122 frames_per_packet, // Minimum Callback level ignored for capture streams.
123 CRAS_STREAM_TYPE_DEFAULT,
124 0, // Unused flags.
125 this,
126 CrasInputStream::SamplesReady,
127 CrasInputStream::StreamError,
128 audio_format);
129 if (!stream_params) {
130 DLOG(WARNING) << "Error setting up stream parameters.";
131 callback_->OnError(this, -ENOMEM);
132 callback_ = NULL;
133 cras_audio_format_destroy(audio_format);
134 return;
135 }
136
137 // Before starting the stream, save the number of bytes in a frame for use in
138 // the callback.
139 bytes_per_frame_ = cras_client_format_bytes_per_frame(audio_format);
140
141 // Adding the stream will start the audio callbacks.
142 if (cras_client_add_stream(client_, &stream_id_, stream_params) == 0) {
143 audio_manager_->IncreaseActiveInputStreamCount();
144 } else {
145 DLOG(WARNING) << "Failed to add the stream.";
146 callback_->OnError(this, -EIO);
147 callback_ = NULL;
148 }
149
150 // Done with config params.
151 cras_audio_format_destroy(audio_format);
152 cras_client_stream_params_destroy(stream_params);
153 }
154
155 void CrasInputStream::Stop() {
156 DCHECK(client_);
157
158 if (!callback_)
159 return;
160
161 // Removing the stream from the client stops audio.
162 cras_client_rm_stream(client_, stream_id_);
163
164 audio_manager_->DecreaseActiveInputStreamCount();
165
166 callback_->OnClose(this);
167 callback_ = NULL;
no longer working on chromium 2012/06/26 21:42:50 I think we should keep OnClose callback in Close()
dgreid 2012/06/27 07:58:07 Done.
168 }
169
170 // Static callback asking for samples. Run on high priority thread.
171 int CrasInputStream::SamplesReady(cras_client* client,
172 cras_stream_id_t stream_id,
173 uint8* samples,
174 size_t frames,
175 const timespec* sample_ts,
176 void* arg) {
177 CrasInputStream* me = static_cast<CrasInputStream*>(arg);
178 me->ReadAudio(frames, samples, sample_ts);
179 return frames;
180 }
181
182 // Static callback for stream errors.
183 int CrasInputStream::StreamError(cras_client* client,
184 cras_stream_id_t stream_id,
185 int err,
186 void* arg) {
187 CrasInputStream* me = static_cast<CrasInputStream*>(arg);
188 me->NotifyStreamError(err);
189 return 0;
190 }
191
192 void CrasInputStream::ReadAudio(size_t frames,
193 uint8* buffer,
194 const timespec* sample_ts) {
195 DCHECK(callback_);
196
197 timespec latency_ts = {0, 0};
198
199 // Determine latency and pass that on to the sink. sample_ts is the wall time
200 // indicating when the first sample in the buffer was captured. Convert that
201 // to latency in bytes.
202 cras_client_calc_capture_latency(sample_ts, &latency_ts);
203 double latency_usec =
204 latency_ts.tv_sec * base::Time::kMicrosecondsPerSecond +
205 latency_ts.tv_nsec * base::Time::kNanosecondsPerMicrosecond;
206 double frames_latency =
207 latency_usec * params_.sample_rate() / base::Time::kMicrosecondsPerSecond;
208 unsigned int bytes_latency =
209 static_cast<unsigned int>(frames_latency * bytes_per_frame_);
210
211 // Update the AGC volume level once every second. Note that, |volume| is
212 // also updated each time SetVolume() is called through IPC by the
213 // render-side AGC.
214 double normalized_volume = 0.0;
215 QueryAgcVolume(&normalized_volume);
216
217 callback_->OnData(this,
218 buffer,
219 frames * bytes_per_frame_,
220 bytes_latency,
221 normalized_volume);
222 }
223
224 void CrasInputStream::NotifyStreamError(int err) {
225 if (callback_)
226 callback_->OnError(this, err);
227 }
228
229 double CrasInputStream::GetMaxVolume() {
230 DCHECK(client_);
231
232 // Return the max volume as a double to match what the alsa input does.
233 return static_cast<double>(cras_client_get_system_max_capture_gain(client_));
234 }
235
236 void CrasInputStream::SetVolume(double volume) {
237 DCHECK(client_);
238
239 cras_client_set_system_capture_gain(client_, static_cast<long>(volume));
240
241 // Update the AGC volume level based on the last setting above. Note that,
242 // the volume-level resolution is not infinite and it is therefore not
243 // possible to assume that the volume provided as input parameter can be
244 // used directly. Instead, a new query to the audio hardware is required.
245 // This method does nothing if AGC is disabled.
246 UpdateAgcVolume();
247 }
248
249 double CrasInputStream::GetVolume() {
250 if (!client_)
251 return 0.0;
252
253 long current_volume = cras_client_get_system_capture_gain(client_);
254
255 // Only want the gain, limit reported level to zero.
256 if (current_volume < 0)
257 return 0.0;
258
259 return static_cast<double>(current_volume);
scherkus (not reviewing) 2012/06/26 22:10:10 maddening!! want to add some docs to say this is i
dgreid 2012/06/27 07:58:07 It is confusing. I should have thought about this
260 }
261
262 } // namespace media
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698