• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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_input.h"
6 
7 #include <math.h>
8 
9 #include "base/basictypes.h"
10 #include "base/logging.h"
11 #include "base/time/time.h"
12 #include "media/audio/audio_manager.h"
13 #include "media/audio/cras/audio_manager_cras.h"
14 
15 namespace media {
16 
CrasInputStream(const AudioParameters & params,AudioManagerCras * manager,const std::string & device_id)17 CrasInputStream::CrasInputStream(const AudioParameters& params,
18                                  AudioManagerCras* manager,
19                                  const std::string& device_id)
20     : audio_manager_(manager),
21       bytes_per_frame_(0),
22       callback_(NULL),
23       client_(NULL),
24       params_(params),
25       started_(false),
26       stream_id_(0),
27       stream_direction_(device_id == AudioManagerBase::kLoopbackInputDeviceId ?
28                             CRAS_STREAM_POST_MIX_PRE_DSP : CRAS_STREAM_INPUT) {
29   DCHECK(audio_manager_);
30   audio_bus_ = AudioBus::Create(params_);
31 }
32 
~CrasInputStream()33 CrasInputStream::~CrasInputStream() {
34   DCHECK(!client_);
35 }
36 
Open()37 bool CrasInputStream::Open() {
38   if (client_) {
39     NOTREACHED() << "CrasInputStream already open";
40     return false;  // Already open.
41   }
42 
43   // Sanity check input values.
44   if (params_.sample_rate() <= 0) {
45     DLOG(WARNING) << "Unsupported audio frequency.";
46     return false;
47   }
48 
49   if (AudioParameters::AUDIO_PCM_LINEAR != params_.format() &&
50       AudioParameters::AUDIO_PCM_LOW_LATENCY != params_.format()) {
51     DLOG(WARNING) << "Unsupported audio format.";
52     return false;
53   }
54 
55   snd_pcm_format_t pcm_format =
56       AudioManagerCras::BitsToFormat(params_.bits_per_sample());
57   if (pcm_format == SND_PCM_FORMAT_UNKNOWN) {
58     DLOG(WARNING) << "Unsupported bits/sample: " << params_.bits_per_sample();
59     return false;
60   }
61 
62   // Create the client and connect to the CRAS server.
63   if (cras_client_create(&client_) < 0) {
64     DLOG(WARNING) << "Couldn't create CRAS client.\n";
65     client_ = NULL;
66     return false;
67   }
68 
69   if (cras_client_connect(client_)) {
70     DLOG(WARNING) << "Couldn't connect CRAS client.\n";
71     cras_client_destroy(client_);
72     client_ = NULL;
73     return false;
74   }
75 
76   // Then start running the client.
77   if (cras_client_run_thread(client_)) {
78     DLOG(WARNING) << "Couldn't run CRAS client.\n";
79     cras_client_destroy(client_);
80     client_ = NULL;
81     return false;
82   }
83 
84   return true;
85 }
86 
Close()87 void CrasInputStream::Close() {
88   Stop();
89 
90   if (client_) {
91     cras_client_stop(client_);
92     cras_client_destroy(client_);
93     client_ = NULL;
94   }
95 
96   // Signal to the manager that we're closed and can be removed.
97   // Should be last call in the method as it deletes "this".
98   audio_manager_->ReleaseInputStream(this);
99 }
100 
Start(AudioInputCallback * callback)101 void CrasInputStream::Start(AudioInputCallback* callback) {
102   DCHECK(client_);
103   DCHECK(callback);
104 
105   // Channel map to CRAS_CHANNEL, values in the same order of
106   // corresponding source in Chromium defined Channels.
107   static const int kChannelMap[] = {
108     CRAS_CH_FL,
109     CRAS_CH_FR,
110     CRAS_CH_FC,
111     CRAS_CH_LFE,
112     CRAS_CH_RL,
113     CRAS_CH_RR,
114     CRAS_CH_FLC,
115     CRAS_CH_FRC,
116     CRAS_CH_RC,
117     CRAS_CH_SL,
118     CRAS_CH_SR
119   };
120   COMPILE_ASSERT(arraysize(kChannelMap) == CHANNELS_MAX + 1,
121                  channel_map_size_do_not_match);
122 
123   // If already playing, stop before re-starting.
124   if (started_)
125     return;
126 
127   StartAgc();
128 
129   callback_ = callback;
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       AudioManagerCras::BitsToFormat(params_.bits_per_sample()),
135       params_.sample_rate(),
136       params_.channels());
137   if (!audio_format) {
138     DLOG(WARNING) << "Error setting up audio parameters.";
139     callback_->OnError(this);
140     callback_ = NULL;
141     return;
142   }
143 
144   // Initialize channel layout to all -1 to indicate that none of
145   // the channels is set in the layout.
146   int8 layout[CRAS_CH_MAX];
147   for (size_t i = 0; i < arraysize(layout); ++i)
148     layout[i] = -1;
149 
150   // Converts to CRAS defined channels. ChannelOrder will return -1
151   // for channels that are not present in params_.channel_layout().
152   for (size_t i = 0; i < arraysize(kChannelMap); ++i) {
153     layout[kChannelMap[i]] = ChannelOrder(params_.channel_layout(),
154                                           static_cast<Channels>(i));
155   }
156   if (cras_audio_format_set_channel_layout(audio_format, layout) != 0) {
157     DLOG(WARNING) << "Error setting channel layout.";
158     callback->OnError(this);
159     return;
160   }
161 
162   unsigned int frames_per_packet = params_.frames_per_buffer();
163   cras_stream_params* stream_params = cras_client_stream_params_create(
164       stream_direction_,
165       frames_per_packet,  // Total latency.
166       frames_per_packet,  // Call back when this many ready.
167       frames_per_packet,  // Minimum Callback level ignored for capture streams.
168       CRAS_STREAM_TYPE_DEFAULT,
169       0,  // Unused flags.
170       this,
171       CrasInputStream::SamplesReady,
172       CrasInputStream::StreamError,
173       audio_format);
174   if (!stream_params) {
175     DLOG(WARNING) << "Error setting up stream parameters.";
176     callback_->OnError(this);
177     callback_ = NULL;
178     cras_audio_format_destroy(audio_format);
179     return;
180   }
181 
182   // Before starting the stream, save the number of bytes in a frame for use in
183   // the callback.
184   bytes_per_frame_ = cras_client_format_bytes_per_frame(audio_format);
185 
186   // Adding the stream will start the audio callbacks.
187   if (cras_client_add_stream(client_, &stream_id_, stream_params)) {
188     DLOG(WARNING) << "Failed to add the stream.";
189     callback_->OnError(this);
190     callback_ = NULL;
191   }
192 
193   // Done with config params.
194   cras_audio_format_destroy(audio_format);
195   cras_client_stream_params_destroy(stream_params);
196 
197   started_ = true;
198 }
199 
Stop()200 void CrasInputStream::Stop() {
201   DCHECK(client_);
202 
203   if (!callback_ || !started_)
204     return;
205 
206   StopAgc();
207 
208   // Removing the stream from the client stops audio.
209   cras_client_rm_stream(client_, stream_id_);
210 
211   started_ = false;
212   callback_ = NULL;
213 }
214 
215 // Static callback asking for samples.  Run on high priority thread.
SamplesReady(cras_client * client,cras_stream_id_t stream_id,uint8 * samples,size_t frames,const timespec * sample_ts,void * arg)216 int CrasInputStream::SamplesReady(cras_client* client,
217                                   cras_stream_id_t stream_id,
218                                   uint8* samples,
219                                   size_t frames,
220                                   const timespec* sample_ts,
221                                   void* arg) {
222   CrasInputStream* me = static_cast<CrasInputStream*>(arg);
223   me->ReadAudio(frames, samples, sample_ts);
224   return frames;
225 }
226 
227 // Static callback for stream errors.
StreamError(cras_client * client,cras_stream_id_t stream_id,int err,void * arg)228 int CrasInputStream::StreamError(cras_client* client,
229                                  cras_stream_id_t stream_id,
230                                  int err,
231                                  void* arg) {
232   CrasInputStream* me = static_cast<CrasInputStream*>(arg);
233   me->NotifyStreamError(err);
234   return 0;
235 }
236 
ReadAudio(size_t frames,uint8 * buffer,const timespec * sample_ts)237 void CrasInputStream::ReadAudio(size_t frames,
238                                 uint8* buffer,
239                                 const timespec* sample_ts) {
240   DCHECK(callback_);
241 
242   timespec latency_ts = {0, 0};
243 
244   // Determine latency and pass that on to the sink.  sample_ts is the wall time
245   // indicating when the first sample in the buffer was captured.  Convert that
246   // to latency in bytes.
247   cras_client_calc_capture_latency(sample_ts, &latency_ts);
248   double latency_usec =
249       latency_ts.tv_sec * base::Time::kMicrosecondsPerSecond +
250       latency_ts.tv_nsec / base::Time::kNanosecondsPerMicrosecond;
251   double frames_latency =
252       latency_usec * params_.sample_rate() / base::Time::kMicrosecondsPerSecond;
253   unsigned int bytes_latency =
254       static_cast<unsigned int>(frames_latency * bytes_per_frame_);
255 
256   // Update the AGC volume level once every second. Note that, |volume| is
257   // also updated each time SetVolume() is called through IPC by the
258   // render-side AGC.
259   double normalized_volume = 0.0;
260   GetAgcVolume(&normalized_volume);
261 
262   audio_bus_->FromInterleaved(
263       buffer, audio_bus_->frames(), params_.bits_per_sample() / 8);
264   callback_->OnData(this, audio_bus_.get(), bytes_latency, normalized_volume);
265 }
266 
NotifyStreamError(int err)267 void CrasInputStream::NotifyStreamError(int err) {
268   if (callback_)
269     callback_->OnError(this);
270 }
271 
GetMaxVolume()272 double CrasInputStream::GetMaxVolume() {
273   DCHECK(client_);
274 
275   // Capture gain is returned as dB * 100 (150 => 1.5dBFS).  Convert the dB
276   // value to a ratio before returning.
277   double dB = cras_client_get_system_max_capture_gain(client_) / 100.0;
278   return GetVolumeRatioFromDecibels(dB);
279 }
280 
SetVolume(double volume)281 void CrasInputStream::SetVolume(double volume) {
282   DCHECK(client_);
283 
284   // Convert from the passed volume ratio, to dB * 100.
285   double dB = GetDecibelsFromVolumeRatio(volume);
286   cras_client_set_system_capture_gain(client_, static_cast<long>(dB * 100.0));
287 
288   // Update the AGC volume level based on the last setting above. Note that,
289   // the volume-level resolution is not infinite and it is therefore not
290   // possible to assume that the volume provided as input parameter can be
291   // used directly. Instead, a new query to the audio hardware is required.
292   // This method does nothing if AGC is disabled.
293   UpdateAgcVolume();
294 }
295 
GetVolume()296 double CrasInputStream::GetVolume() {
297   if (!client_)
298     return 0.0;
299 
300   long dB = cras_client_get_system_capture_gain(client_) / 100.0;
301   return GetVolumeRatioFromDecibels(dB);
302 }
303 
IsMuted()304 bool CrasInputStream::IsMuted() {
305   return false;
306 }
307 
GetVolumeRatioFromDecibels(double dB) const308 double CrasInputStream::GetVolumeRatioFromDecibels(double dB) const {
309   return pow(10, dB / 20.0);
310 }
311 
GetDecibelsFromVolumeRatio(double volume_ratio) const312 double CrasInputStream::GetDecibelsFromVolumeRatio(double volume_ratio) const {
313   return 20 * log10(volume_ratio);
314 }
315 
316 }  // namespace media
317