Chromium Code Reviews| OLD | NEW |
|---|---|
| (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 frame_rate_(params.sample_rate()), | |
|
no longer working on chromium
2012/06/20 10:45:26
How about changing the name to sample_rate_?
We d
dgreid
2012/06/20 17:14:25
Done.
| |
| 24 num_channels_(params.channels()), | |
| 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() {} | |
|
no longer working on chromium
2012/06/20 10:45:26
Add a DCHECK here to make sure things have already
dgreid
2012/06/20 17:14:25
Done.
| |
| 32 | |
| 33 bool CrasInputStream::Open() { | |
| 34 if (client_) { | |
| 35 NOTREACHED() << "CrasInputStream already open"; | |
| 36 return false; // Already open. | |
| 37 } | |
| 38 | |
| 39 // Sanity check input values. | |
| 40 if (params_.sample_rate() <= 0) { | |
| 41 LOG(WARNING) << "Unsupported audio frequency."; | |
|
no longer working on chromium
2012/06/20 10:45:26
I know we use LOG for alsa too, but is it really n
dgreid
2012/06/20 17:14:25
Done for whole file.
| |
| 42 return false; | |
| 43 } | |
| 44 | |
| 45 if (AudioParameters::AUDIO_PCM_LINEAR != params_.format() && | |
| 46 AudioParameters::AUDIO_PCM_LOW_LATENCY != params_.format()) { | |
| 47 LOG(WARNING) << "Unsupported audio format."; | |
|
no longer working on chromium
2012/06/20 10:45:26
ditto
| |
| 48 return false; | |
| 49 } | |
| 50 | |
| 51 if (pcm_format_ == SND_PCM_FORMAT_UNKNOWN) { | |
| 52 LOG(WARNING) << "Unsupported bits/sample: " << params_.bits_per_sample(); | |
|
no longer working on chromium
2012/06/20 10:45:26
ditto
| |
| 53 return false; | |
| 54 } | |
| 55 | |
| 56 // Create the client and connect to the CRAS server. | |
| 57 if (cras_client_create(&client_) < 0) { | |
| 58 LOG(WARNING) << "Couldn't create CRAS client.\n"; | |
|
no longer working on chromium
2012/06/20 10:45:26
ditto
| |
| 59 client_ = NULL; | |
| 60 return false; | |
| 61 } | |
| 62 | |
| 63 if (cras_client_connect(client_)) { | |
| 64 LOG(WARNING) << "Couldn't connect CRAS client.\n"; | |
|
no longer working on chromium
2012/06/20 10:45:26
ditto
| |
| 65 cras_client_destroy(client_); | |
| 66 client_ = NULL; | |
| 67 return false; | |
| 68 } | |
| 69 | |
| 70 // Then start running the client. | |
| 71 if (cras_client_run_thread(client_)) { | |
| 72 LOG(WARNING) << "Couldn't run CRAS client.\n"; | |
| 73 cras_client_destroy(client_); | |
| 74 client_ = NULL; | |
| 75 return false; | |
| 76 } | |
| 77 | |
| 78 return true; | |
| 79 } | |
| 80 | |
| 81 void CrasInputStream::Close() { | |
| 82 if (client_) { | |
| 83 cras_client_stop(client_); | |
| 84 cras_client_destroy(client_); | |
| 85 client_ = NULL; | |
| 86 } | |
| 87 | |
| 88 if (callback_) | |
| 89 callback_->OnClose(this); | |
| 90 | |
| 91 // Signal to the manager that we're closed and can be removed. | |
| 92 // Should be last call in the method as it deletes "this". | |
| 93 audio_manager_->ReleaseInputStream(this); | |
| 94 } | |
| 95 | |
| 96 void CrasInputStream::Start(AudioInputCallback* callback) { | |
| 97 DCHECK(client_); | |
| 98 DCHECK(!callback_ && callback); | |
|
no longer working on chromium
2012/06/20 10:45:26
Does this mean we only allow calling Start() once?
dgreid
2012/06/20 17:14:25
Yes, call Start once until Stop is called. This s
| |
| 99 callback_ = callback; | |
| 100 | |
| 101 // Prepare |audio_format| and |stream_params| for the stream we | |
| 102 // will create. | |
| 103 cras_audio_format* audio_format = cras_audio_format_create( | |
| 104 pcm_format_, | |
| 105 frame_rate_, | |
| 106 num_channels_); | |
| 107 if (!audio_format) { | |
| 108 LOG(WARNING) << "Error setting up audio parameters."; | |
| 109 callback->OnError(this, -ENOMEM); | |
| 110 return; | |
| 111 } | |
| 112 | |
| 113 unsigned int frames_per_packet = params_.frames_per_buffer(); | |
| 114 cras_stream_params* stream_params = cras_client_stream_params_create( | |
| 115 CRAS_STREAM_INPUT, | |
| 116 frames_per_packet, // Total latency. | |
|
no longer working on chromium
2012/06/20 10:45:26
Curiously, does it mean we are using one queue wit
dgreid
2012/06/20 17:14:25
On the server side there are two buffers of this s
| |
| 117 frames_per_packet, // Call back when this many ready. | |
| 118 frames_per_packet, // Don't care for capture. | |
|
no longer working on chromium
2012/06/20 10:45:26
I am curious, what does this parameter mean?
dgreid
2012/06/20 17:14:25
For an output stream, it indicates the minimum amo
| |
| 119 CRAS_STREAM_TYPE_DEFAULT, | |
| 120 0, // Unused flags. | |
| 121 this, | |
| 122 CrasInputStream::SamplesReady, | |
| 123 CrasInputStream::StreamError, | |
| 124 audio_format); | |
| 125 if (!stream_params) { | |
| 126 LOG(WARNING) << "Error setting up stream parameters."; | |
| 127 callback->OnError(this, -ENOMEM); | |
| 128 cras_audio_format_destroy(audio_format); | |
| 129 return; | |
| 130 } | |
| 131 | |
| 132 // Before starting the stream, save the number of bytes in a frame for use in | |
| 133 // the callback. | |
| 134 bytes_per_frame_ = cras_client_format_bytes_per_frame(audio_format); | |
| 135 | |
| 136 // Adding the stream will start the audio callbacks. | |
| 137 if (cras_client_add_stream(client_, &stream_id_, stream_params) == 0) { | |
| 138 audio_manager_->IncreaseActiveInputStreamCount(); | |
|
no longer working on chromium
2012/06/20 10:45:26
In case Start() fails for any of the reason, shoul
dgreid
2012/06/20 17:14:25
Thanks, good catch. Made them match up, so that c
| |
| 139 } else { | |
| 140 LOG(WARNING) << "Failed to add the stream"; | |
|
no longer working on chromium
2012/06/20 10:45:26
DLOG, and add a period.
dgreid
2012/06/20 17:14:25
Done.
| |
| 141 callback->OnError(this, -EIO); | |
| 142 } | |
| 143 | |
| 144 // Done with config params. | |
| 145 cras_audio_format_destroy(audio_format); | |
| 146 cras_client_stream_params_destroy(stream_params); | |
| 147 } | |
| 148 | |
| 149 void CrasInputStream::Stop() { | |
| 150 if (!client_ || !callback_) | |
| 151 return; | |
| 152 | |
| 153 // Removing the stream from the client stops audio. | |
| 154 cras_client_rm_stream(client_, stream_id_); | |
| 155 | |
| 156 audio_manager_->DecreaseActiveInputStreamCount(); | |
| 157 } | |
| 158 | |
| 159 // Static callback asking for samples. Run on high priority thread. | |
| 160 int CrasInputStream::SamplesReady(cras_client* client, | |
| 161 cras_stream_id_t stream_id, | |
| 162 uint8* samples, | |
| 163 size_t frames, | |
| 164 const timespec* sample_ts, | |
| 165 void* arg) { | |
| 166 CrasInputStream* me = static_cast<CrasInputStream*>(arg); | |
| 167 me->ReadAudio(frames, samples, sample_ts); | |
| 168 return frames; | |
| 169 } | |
| 170 | |
| 171 // Static callback for stream errors. | |
| 172 int CrasInputStream::StreamError(cras_client* client, | |
| 173 cras_stream_id_t stream_id, | |
| 174 int err, | |
| 175 void* arg) { | |
| 176 CrasInputStream* me = static_cast<CrasInputStream*>(arg); | |
| 177 me->NotifyStreamError(err); | |
| 178 return 0; | |
| 179 } | |
| 180 | |
| 181 void CrasInputStream::ReadAudio(size_t frames, | |
| 182 uint8* buffer, | |
| 183 const timespec* sample_ts) { | |
| 184 DCHECK(callback_); | |
| 185 | |
| 186 timespec latency_ts = {0, 0}; | |
| 187 | |
| 188 // Determine latency and pass that on to the sink. sample_ts is the wall time | |
| 189 // indicating when the first sample in the buffer was captured. Convert that | |
| 190 // to latency in bytes. | |
| 191 cras_client_calc_capture_latency(sample_ts, &latency_ts); | |
| 192 double latency_usec = (latency_ts.tv_sec * 1e6) + latency_ts.tv_nsec * 1e-3; | |
| 193 double frames_latency = latency_usec * frame_rate_ * 1e-6; | |
| 194 unsigned int bytes_latency = | |
| 195 static_cast<unsigned int>(frames_latency * bytes_per_frame_); | |
| 196 | |
| 197 // Update the AGC volume level once every second. Note that, |volume| is | |
| 198 // also updated each time SetVolume() is called through IPC by the | |
| 199 // render-side AGC. | |
| 200 double normalized_volume = 0.0; | |
| 201 QueryAgcVolume(&normalized_volume); | |
| 202 | |
| 203 callback_->OnData(this, | |
| 204 buffer, | |
| 205 frames * bytes_per_frame_, | |
| 206 bytes_latency, | |
| 207 normalized_volume); | |
| 208 } | |
| 209 | |
| 210 void CrasInputStream::NotifyStreamError(int err) { | |
| 211 if (callback_) | |
| 212 callback_->OnError(this, err); | |
| 213 } | |
| 214 | |
| 215 double CrasInputStream::GetMaxVolume() { | |
| 216 DCHECK(client_); | |
| 217 | |
| 218 // Return the max volume as a double to match what the alsa input does. | |
| 219 return static_cast<double>(cras_client_get_system_max_capture_gain(client_)); | |
| 220 } | |
| 221 | |
| 222 void CrasInputStream::SetVolume(double volume) { | |
| 223 DCHECK(client_); | |
| 224 | |
| 225 cras_client_set_system_capture_gain(client_, static_cast<long>(volume)); | |
| 226 | |
| 227 // Update the AGC volume level based on the last setting above. Note that, | |
| 228 // the volume-level resolution is not infinite and it is therefore not | |
| 229 // possible to assume that the volume provided as input parameter can be | |
| 230 // used directly. Instead, a new query to the audio hardware is required. | |
| 231 // This method does nothing if AGC is disabled. | |
| 232 UpdateAgcVolume(); | |
| 233 } | |
| 234 | |
| 235 double CrasInputStream::GetVolume() { | |
| 236 if (!client_) | |
| 237 return 0.0; | |
| 238 | |
| 239 long current_volume = cras_client_get_system_capture_gain(client_); | |
| 240 | |
| 241 // Only want the gain, limit reported level to zero. | |
| 242 if (current_volume < 0) | |
| 243 return 0.0; | |
| 244 | |
| 245 return static_cast<double>(current_volume); | |
| 246 } | |
| 247 | |
| 248 } // namespace media | |
| OLD | NEW |