| 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/pulse/pulse_input.h" | 
 |    6  | 
 |    7 #include <pulse/pulseaudio.h> | 
 |    8  | 
 |    9 #include "base/logging.h" | 
 |   10 #include "base/message_loop.h" | 
 |   11 #include "media/audio/pulse/audio_manager_pulse.h" | 
 |   12 #include "media/audio/pulse/pulse_util.h" | 
 |   13 #include "media/base/seekable_buffer.h" | 
 |   14  | 
 |   15 namespace media { | 
 |   16  | 
 |   17 using pulse::AutoPulseLock; | 
 |   18 using pulse::WaitForOperationCompletion; | 
 |   19  | 
 |   20 PulseAudioInputStream::PulseAudioInputStream(AudioManagerPulse* audio_manager, | 
 |   21                                              const std::string& device_name, | 
 |   22                                              const AudioParameters& params, | 
 |   23                                              pa_threaded_mainloop* mainloop, | 
 |   24                                              pa_context* context) | 
 |   25     : audio_manager_(audio_manager), | 
 |   26       callback_(NULL), | 
 |   27       device_name_(device_name), | 
 |   28       params_(params), | 
 |   29       channels_(0), | 
 |   30       volume_(0.0), | 
 |   31       stream_started_(false), | 
 |   32       pa_mainloop_(mainloop), | 
 |   33       pa_context_(context), | 
 |   34       handle_(NULL), | 
 |   35       context_state_changed_(false) { | 
 |   36   DCHECK(audio_manager_->GetMessageLoop()->BelongsToCurrentThread()); | 
 |   37   DCHECK(mainloop); | 
 |   38   DCHECK(context); | 
 |   39 } | 
 |   40  | 
 |   41 PulseAudioInputStream::~PulseAudioInputStream() { | 
 |   42   // All internal structures should already have been freed in Close(), | 
 |   43   // which calls AudioManagerPulse::Release which deletes this object. | 
 |   44   DCHECK(!handle_); | 
 |   45 } | 
 |   46  | 
 |   47 bool PulseAudioInputStream::Open() { | 
 |   48   DCHECK(audio_manager_->GetMessageLoop()->BelongsToCurrentThread()); | 
 |   49   AutoPulseLock auto_lock(pa_mainloop_); | 
 |   50  | 
 |   51   // Set sample specifications. | 
 |   52   pa_sample_spec pa_sample_specifications; | 
 |   53   pa_sample_specifications.format = pulse::BitsToPASampleFormat( | 
 |   54       params_.bits_per_sample()); | 
 |   55   pa_sample_specifications.rate = params_.sample_rate(); | 
 |   56   pa_sample_specifications.channels = params_.channels(); | 
 |   57  | 
 |   58   // Get channel mapping and open recording stream. | 
 |   59   pa_channel_map source_channel_map = pulse::ChannelLayoutToPAChannelMap( | 
 |   60       params_.channel_layout()); | 
 |   61   pa_channel_map* map = (source_channel_map.channels != 0)? | 
 |   62       &source_channel_map : NULL; | 
 |   63  | 
 |   64   // Create a new recording stream. | 
 |   65   handle_ = pa_stream_new(pa_context_, "RecordStream", | 
 |   66                           &pa_sample_specifications, map); | 
 |   67   if (!handle_) { | 
 |   68     DLOG(ERROR) << "Open: failed to create PA stream"; | 
 |   69     return false; | 
 |   70   } | 
 |   71  | 
 |   72   pa_stream_set_state_callback(handle_, &StreamNotifyCallback, this); | 
 |   73  | 
 |   74   // Set server-side capture buffer metrics. Detailed documentation on what | 
 |   75   // values should be chosen can be found at | 
 |   76   // freedesktop.org/software/pulseaudio/doxygen/structpa__buffer__attr.html. | 
 |   77   pa_buffer_attr buffer_attributes; | 
 |   78   const unsigned int buffer_size = params_.GetBytesPerBuffer(); | 
 |   79   buffer_attributes.maxlength =  static_cast<uint32_t>(-1); | 
 |   80   buffer_attributes.tlength = buffer_size; | 
 |   81   buffer_attributes.minreq = buffer_size; | 
 |   82   buffer_attributes.prebuf = static_cast<uint32_t>(-1); | 
 |   83   buffer_attributes.fragsize = buffer_size; | 
 |   84   int flags = PA_STREAM_AUTO_TIMING_UPDATE | | 
 |   85               PA_STREAM_INTERPOLATE_TIMING | | 
 |   86               PA_STREAM_ADJUST_LATENCY | | 
 |   87               PA_STREAM_START_CORKED; | 
 |   88   int err = pa_stream_connect_record( | 
 |   89       handle_, | 
 |   90       device_name_ == AudioManagerBase::kDefaultDeviceId ? | 
 |   91           NULL : device_name_.c_str(), | 
 |   92       &buffer_attributes, | 
 |   93       static_cast<pa_stream_flags_t>(flags)); | 
 |   94   if (err) { | 
 |   95     DLOG(ERROR) << "pa_stream_connect_playback FAILED " << err; | 
 |   96     return false; | 
 |   97   } | 
 |   98  | 
 |   99   // Wait for the stream to be ready. | 
 |  100   while (true) { | 
 |  101     pa_stream_state_t stream_state = pa_stream_get_state(handle_); | 
 |  102     if(!PA_STREAM_IS_GOOD(stream_state)) { | 
 |  103       DLOG(ERROR) << "Invalid PulseAudio stream state"; | 
 |  104       return false; | 
 |  105     } | 
 |  106  | 
 |  107     if (stream_state == PA_STREAM_READY) | 
 |  108       break; | 
 |  109     pa_threaded_mainloop_wait(pa_mainloop_); | 
 |  110   } | 
 |  111  | 
 |  112   pa_stream_set_read_callback(handle_, &ReadCallback, this); | 
 |  113   pa_stream_readable_size(handle_); | 
 |  114  | 
 |  115   buffer_.reset(new media::SeekableBuffer(0, 2 * params_.GetBytesPerBuffer())); | 
 |  116   audio_data_buffer_.reset(new uint8[params_.GetBytesPerBuffer()]); | 
 |  117   return true; | 
 |  118 } | 
 |  119  | 
 |  120 void PulseAudioInputStream::Start(AudioInputCallback* callback) { | 
 |  121   DCHECK(audio_manager_->GetMessageLoop()->BelongsToCurrentThread()); | 
 |  122   DCHECK(callback); | 
 |  123   DCHECK(handle_); | 
 |  124   AutoPulseLock auto_lock(pa_mainloop_); | 
 |  125  | 
 |  126   if (stream_started_) | 
 |  127     return; | 
 |  128  | 
 |  129   // Clean up the old buffer. | 
 |  130   pa_stream_drop(handle_); | 
 |  131   buffer_->Clear(); | 
 |  132  | 
 |  133   // Start the streaming. | 
 |  134   stream_started_ = true; | 
 |  135   callback_ = callback; | 
 |  136  | 
 |  137   pa_operation* operation = pa_stream_cork(handle_, 0, NULL, NULL); | 
 |  138   WaitForOperationCompletion(pa_mainloop_, operation); | 
 |  139 } | 
 |  140  | 
 |  141 void PulseAudioInputStream::Stop() { | 
 |  142   DCHECK(audio_manager_->GetMessageLoop()->BelongsToCurrentThread()); | 
 |  143   AutoPulseLock auto_lock(pa_mainloop_); | 
 |  144   if (!stream_started_) | 
 |  145     return; | 
 |  146  | 
 |  147   // Set the flag to false to stop filling new data to soundcard. | 
 |  148   stream_started_ = false; | 
 |  149  | 
 |  150   pa_operation* operation = pa_stream_flush( | 
 |  151       handle_, &pulse::StreamSuccessCallback, pa_mainloop_); | 
 |  152   WaitForOperationCompletion(pa_mainloop_, operation); | 
 |  153  | 
 |  154   // Stop the stream. | 
 |  155   pa_stream_set_read_callback(handle_, NULL, NULL); | 
 |  156   operation = pa_stream_cork(handle_, 1, &pulse::StreamSuccessCallback, | 
 |  157                              pa_mainloop_); | 
 |  158   WaitForOperationCompletion(pa_mainloop_, operation); | 
 |  159 } | 
 |  160  | 
 |  161 void PulseAudioInputStream::Close() { | 
 |  162   DCHECK(audio_manager_->GetMessageLoop()->BelongsToCurrentThread()); | 
 |  163   { | 
 |  164     AutoPulseLock auto_lock(pa_mainloop_); | 
 |  165     if (handle_) { | 
 |  166       // Disable all the callbacks before disconnecting. | 
 |  167       pa_stream_set_state_callback(handle_, NULL, NULL); | 
 |  168       pa_stream_flush(handle_, NULL, NULL); | 
 |  169  | 
 |  170       if (pa_stream_get_state(handle_) != PA_STREAM_UNCONNECTED) | 
 |  171         pa_stream_disconnect(handle_); | 
 |  172  | 
 |  173       // Release PulseAudio structures. | 
 |  174       pa_stream_unref(handle_); | 
 |  175       handle_ = NULL; | 
 |  176     } | 
 |  177   } | 
 |  178  | 
 |  179   if (callback_) | 
 |  180     callback_->OnClose(this); | 
 |  181  | 
 |  182   // Signal to the manager that we're closed and can be removed. | 
 |  183   // This should be the last call in the function as it deletes "this". | 
 |  184   audio_manager_->ReleaseInputStream(this); | 
 |  185 } | 
 |  186  | 
 |  187 double PulseAudioInputStream::GetMaxVolume() { | 
 |  188   return static_cast<double>(PA_VOLUME_NORM); | 
 |  189 } | 
 |  190  | 
 |  191 void PulseAudioInputStream::SetVolume(double volume) { | 
 |  192   AutoPulseLock auto_lock(pa_mainloop_); | 
 |  193   if (!handle_) | 
 |  194     return; | 
 |  195  | 
 |  196   size_t index = pa_stream_get_device_index(handle_); | 
 |  197   pa_operation* operation = NULL; | 
 |  198   if (!channels_) { | 
 |  199     // Get the number of channels for the source only when the |channels_| is 0. | 
 |  200     // We are assuming the stream source is not changed on the fly here. | 
 |  201     operation = pa_context_get_source_info_by_index( | 
 |  202         pa_context_, index, &VolumeCallback, this); | 
 |  203     WaitForOperationCompletion(pa_mainloop_, operation); | 
 |  204     if (!channels_) { | 
 |  205       DLOG(WARNING) << "Failed to get the number of channels for the source"; | 
 |  206       return; | 
 |  207     } | 
 |  208   } | 
 |  209  | 
 |  210   pa_cvolume pa_volume; | 
 |  211   pa_cvolume_set(&pa_volume, channels_, volume); | 
 |  212   operation = pa_context_set_source_volume_by_index( | 
 |  213       pa_context_, index, &pa_volume, NULL, NULL); | 
 |  214  | 
 |  215   // Don't need to wait for this task to complete. | 
 |  216   pa_operation_unref(operation); | 
 |  217 } | 
 |  218  | 
 |  219 double PulseAudioInputStream::GetVolume() { | 
 |  220   AutoPulseLock auto_lock(pa_mainloop_); | 
 |  221   if (!handle_) | 
 |  222     return 0.0; | 
 |  223  | 
 |  224   size_t index = pa_stream_get_device_index(handle_); | 
 |  225   pa_operation* operation = pa_context_get_source_info_by_index( | 
 |  226       pa_context_, index, &VolumeCallback, this); | 
 |  227   WaitForOperationCompletion(pa_mainloop_, operation); | 
 |  228  | 
 |  229   return volume_; | 
 |  230 } | 
 |  231  | 
 |  232 // static, used by pa_stream_set_read_callback. | 
 |  233 void PulseAudioInputStream::ReadCallback(pa_stream* handle, | 
 |  234                                          size_t length, | 
 |  235                                          void* user_data) { | 
 |  236   PulseAudioInputStream* stream = | 
 |  237       reinterpret_cast<PulseAudioInputStream*>(user_data); | 
 |  238  | 
 |  239   stream->ReadData(); | 
 |  240 } | 
 |  241  | 
 |  242 // static, used by pa_context_get_source_info_by_index. | 
 |  243 void PulseAudioInputStream::VolumeCallback(pa_context* context, | 
 |  244                                            const pa_source_info* info, | 
 |  245                                            int error, void* user_data) { | 
 |  246   PulseAudioInputStream* stream = | 
 |  247       reinterpret_cast<PulseAudioInputStream*>(user_data); | 
 |  248  | 
 |  249   if (error) { | 
 |  250     pa_threaded_mainloop_signal(stream->pa_mainloop_, 0); | 
 |  251     return; | 
 |  252   } | 
 |  253  | 
 |  254   if (stream->channels_ != info->channel_map.channels) | 
 |  255     stream->channels_ = info->channel_map.channels; | 
 |  256  | 
 |  257   pa_volume_t volume = PA_VOLUME_MUTED; // Minimum possible value. | 
 |  258   // Use the max volume of any channel as the volume. | 
 |  259   for (int i = 0; i < stream->channels_; ++i) { | 
 |  260     if (volume < info->volume.values[i]) | 
 |  261       volume = info->volume.values[i]; | 
 |  262   } | 
 |  263  | 
 |  264   stream->volume_ = static_cast<double>(volume); | 
 |  265 } | 
 |  266  | 
 |  267 // static, used by pa_stream_set_state_callback. | 
 |  268 void PulseAudioInputStream::StreamNotifyCallback(pa_stream* s, | 
 |  269                                                  void* user_data) { | 
 |  270   PulseAudioInputStream* stream = | 
 |  271       reinterpret_cast<PulseAudioInputStream*>(user_data); | 
 |  272   if (s && stream->callback_ && | 
 |  273       pa_stream_get_state(s) == PA_STREAM_FAILED) { | 
 |  274     stream->callback_->OnError(stream, pa_context_errno(stream->pa_context_)); | 
 |  275   } | 
 |  276  | 
 |  277   pa_threaded_mainloop_signal(stream->pa_mainloop_, 0); | 
 |  278 } | 
 |  279  | 
 |  280 void PulseAudioInputStream::ReadData() { | 
 |  281   uint32 hardware_delay = pulse::GetHardwareLatencyInBytes( | 
 |  282       handle_, params_.sample_rate(), params_.GetBytesPerFrame()); | 
 |  283  | 
 |  284   // Update the AGC volume level once every second. Note that, | 
 |  285   // |volume| is also updated each time SetVolume() is called | 
 |  286   // through IPC by the render-side AGC. | 
 |  287   double normalized_volume = 0.0; | 
 |  288   QueryAgcVolume(&normalized_volume); | 
 |  289  | 
 |  290   do { | 
 |  291     size_t length = 0; | 
 |  292     const void* data = NULL; | 
 |  293     pa_stream_peek(handle_, &data, &length); | 
 |  294     if (!data || length == 0) | 
 |  295       break; | 
 |  296  | 
 |  297     buffer_->Append(reinterpret_cast<const uint8*>(data), length); | 
 |  298  | 
 |  299     // Checks if we still have data. | 
 |  300     pa_stream_drop(handle_); | 
 |  301   } while (pa_stream_readable_size(handle_) > 0); | 
 |  302  | 
 |  303   int packet_size = params_.GetBytesPerBuffer(); | 
 |  304   while (buffer_->forward_bytes() >= packet_size) { | 
 |  305     buffer_->Read(audio_data_buffer_.get(), packet_size); | 
 |  306     callback_->OnData(this, audio_data_buffer_.get(),  packet_size, | 
 |  307                       hardware_delay, normalized_volume); | 
 |  308  | 
 |  309     if (buffer_->forward_bytes() < packet_size) | 
 |  310       break; | 
 |  311  | 
 |  312     // TODO(xians): improve the code by implementing a WaitTillDataReady on the | 
 |  313     // input side. | 
 |  314     DLOG(WARNING) << "OnData is being called consecutively, sleep 5ms to " | 
 |  315                   << "wait until render consumes the data"; | 
 |  316     base::PlatformThread::Sleep( | 
 |  317         base::TimeDelta::FromMilliseconds(5)); | 
 |  318   } | 
 |  319  | 
 |  320   pa_threaded_mainloop_signal(pa_mainloop_, 0); | 
 |  321 } | 
 |  322  | 
 |  323 }  // namespace media | 
| OLD | NEW |