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 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 | |
| OLD | NEW |