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