| OLD | NEW | 
| (Empty) |  | 
 |    1 // Copyright 2013 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_unified.h" | 
 |    6  | 
 |    7 #include <cras_client.h> | 
 |    8  | 
 |    9 #include "base/command_line.h" | 
 |   10 #include "base/logging.h" | 
 |   11 #include "media/audio/audio_util.h" | 
 |   12 #include "media/audio/cras/audio_manager_cras.h" | 
 |   13 #include "media/audio/linux/alsa_util.h" | 
 |   14  | 
 |   15 namespace media { | 
 |   16  | 
 |   17 // Overview of operation: | 
 |   18 // 1) An object of CrasUnifiedStream is created by the AudioManager | 
 |   19 // factory: audio_man->MakeAudioStream(). | 
 |   20 // 2) Next some thread will call Open(), at that point a client is created and | 
 |   21 // configured for the correct format and sample rate. | 
 |   22 // 3) Then Start(source) is called and a stream is added to the CRAS client | 
 |   23 // which will create its own thread that periodically calls the source for more | 
 |   24 // data as buffers are being consumed. | 
 |   25 // 4) When finished Stop() is called, which is handled by stopping the stream. | 
 |   26 // 5) Finally Close() is called. It cleans up and notifies the audio manager, | 
 |   27 // which likely will destroy this object. | 
 |   28 // | 
 |   29 // For output-only streams, a unified stream is created with 0 input channels. | 
 |   30 // | 
 |   31 // Simplified data flow for unified streams: | 
 |   32 // | 
 |   33 //   +-------------+                  +------------------+ | 
 |   34 //   | CRAS Server |                  | Chrome Client    | | 
 |   35 //   +------+------+    Add Stream    +---------+--------+ | 
 |   36 //          |<----------------------------------| | 
 |   37 //          |                                   | | 
 |   38 //          |  buffer_frames captured to shm    | | 
 |   39 //          |---------------------------------->| | 
 |   40 //          |                                   |  UnifiedCallback() | 
 |   41 //          |                                   |  ReadWriteAudio() | 
 |   42 //          |                                   | | 
 |   43 //          |  buffer_frames written to shm     | | 
 |   44 //          |<----------------------------------| | 
 |   45 //          |                                   | | 
 |   46 //         ...  Repeats for each block.        ... | 
 |   47 //          |                                   | | 
 |   48 //          |                                   | | 
 |   49 //          |  Remove stream                    | | 
 |   50 //          |<----------------------------------| | 
 |   51 //          |                                   | | 
 |   52 // | 
 |   53 // Simplified data flow for output only streams: | 
 |   54 // | 
 |   55 //   +-------------+                  +------------------+ | 
 |   56 //   | CRAS Server |                  | Chrome Client    | | 
 |   57 //   +------+------+    Add Stream    +---------+--------+ | 
 |   58 //          |<----------------------------------| | 
 |   59 //          |                                   | | 
 |   60 //          | Near out of samples, request more | | 
 |   61 //          |---------------------------------->| | 
 |   62 //          |                                   |  UnifiedCallback() | 
 |   63 //          |                                   |  WriteAudio() | 
 |   64 //          |                                   | | 
 |   65 //          |  buffer_frames written to shm     | | 
 |   66 //          |<----------------------------------| | 
 |   67 //          |                                   | | 
 |   68 //         ...  Repeats for each block.        ... | 
 |   69 //          |                                   | | 
 |   70 //          |                                   | | 
 |   71 //          |  Remove stream                    | | 
 |   72 //          |<----------------------------------| | 
 |   73 //          |                                   | | 
 |   74 // | 
 |   75 // For Unified streams the Chrome client is notified whenever buffer_frames have | 
 |   76 // been captured.  For Output streams the client is notified a few milliseconds | 
 |   77 // before the hardware buffer underruns and fills the buffer with another block | 
 |   78 // of audio. | 
 |   79  | 
 |   80 CrasUnifiedStream::CrasUnifiedStream(const AudioParameters& params, | 
 |   81                                      AudioManagerCras* manager) | 
 |   82     : client_(NULL), | 
 |   83       stream_id_(0), | 
 |   84       params_(params), | 
 |   85       bytes_per_frame_(0), | 
 |   86       is_playing_(false), | 
 |   87       volume_(1.0), | 
 |   88       manager_(manager), | 
 |   89       source_callback_(NULL), | 
 |   90       input_bus_(NULL), | 
 |   91       output_bus_(NULL), | 
 |   92       stream_direction_(CRAS_STREAM_OUTPUT) { | 
 |   93   DCHECK(manager_); | 
 |   94   DCHECK(params_.channels()  > 0); | 
 |   95  | 
 |   96   // Must have at least one input or output.  If there are both they must be the | 
 |   97   // same. | 
 |   98   int input_channels = params_.input_channels(); | 
 |   99  | 
 |  100   if (input_channels) { | 
 |  101     // A unified stream for input and output. | 
 |  102     DCHECK(params_.channels() == input_channels); | 
 |  103     stream_direction_ = CRAS_STREAM_UNIFIED; | 
 |  104     input_bus_ = AudioBus::Create(input_channels, | 
 |  105                                   params_.frames_per_buffer()); | 
 |  106   } | 
 |  107  | 
 |  108   output_bus_ = AudioBus::Create(params); | 
 |  109 } | 
 |  110  | 
 |  111 CrasUnifiedStream::~CrasUnifiedStream() { | 
 |  112   DCHECK(!is_playing_); | 
 |  113 } | 
 |  114  | 
 |  115 bool CrasUnifiedStream::Open() { | 
 |  116   // Sanity check input values. | 
 |  117   if (params_.sample_rate() <= 0) { | 
 |  118     LOG(WARNING) << "Unsupported audio frequency."; | 
 |  119     return false; | 
 |  120   } | 
 |  121  | 
 |  122   if (alsa_util::BitsToFormat(params_.bits_per_sample()) == | 
 |  123       SND_PCM_FORMAT_UNKNOWN) { | 
 |  124     LOG(WARNING) << "Unsupported pcm format"; | 
 |  125     return false; | 
 |  126   } | 
 |  127  | 
 |  128   // Create the client and connect to the CRAS server. | 
 |  129   if (cras_client_create(&client_)) { | 
 |  130     LOG(WARNING) << "Couldn't create CRAS client.\n"; | 
 |  131     client_ = NULL; | 
 |  132     return false; | 
 |  133   } | 
 |  134  | 
 |  135   if (cras_client_connect(client_)) { | 
 |  136     LOG(WARNING) << "Couldn't connect CRAS client.\n"; | 
 |  137     cras_client_destroy(client_); | 
 |  138     client_ = NULL; | 
 |  139     return false; | 
 |  140   } | 
 |  141  | 
 |  142   // Then start running the client. | 
 |  143   if (cras_client_run_thread(client_)) { | 
 |  144     LOG(WARNING) << "Couldn't run CRAS client.\n"; | 
 |  145     cras_client_destroy(client_); | 
 |  146     client_ = NULL; | 
 |  147     return false; | 
 |  148   } | 
 |  149  | 
 |  150   return true; | 
 |  151 } | 
 |  152  | 
 |  153 void CrasUnifiedStream::Close() { | 
 |  154   if (client_) { | 
 |  155     cras_client_stop(client_); | 
 |  156     cras_client_destroy(client_); | 
 |  157     client_ = NULL; | 
 |  158   } | 
 |  159  | 
 |  160   // Signal to the manager that we're closed and can be removed. | 
 |  161   // Should be last call in the method as it deletes "this". | 
 |  162   manager_->ReleaseOutputStream(this); | 
 |  163 } | 
 |  164  | 
 |  165 void CrasUnifiedStream::Start(AudioSourceCallback* callback) { | 
 |  166   CHECK(callback); | 
 |  167   source_callback_ = callback; | 
 |  168  | 
 |  169   // Only start if we can enter the playing state. | 
 |  170   if (is_playing_) | 
 |  171     return; | 
 |  172  | 
 |  173   LOG(ERROR) << "Unified Start"; | 
 |  174   // Prepare |audio_format| and |stream_params| for the stream we | 
 |  175   // will create. | 
 |  176   cras_audio_format* audio_format = cras_audio_format_create( | 
 |  177       alsa_util::BitsToFormat(params_.bits_per_sample()), | 
 |  178       params_.sample_rate(), | 
 |  179       params_.channels()); | 
 |  180   if (!audio_format) { | 
 |  181     LOG(WARNING) << "Error setting up audio parameters."; | 
 |  182     callback->OnError(this); | 
 |  183     return; | 
 |  184   } | 
 |  185  | 
 |  186   cras_stream_params* stream_params = cras_client_unified_params_create( | 
 |  187       stream_direction_, | 
 |  188       params_.frames_per_buffer(), | 
 |  189       CRAS_STREAM_TYPE_DEFAULT, | 
 |  190       0, | 
 |  191       this, | 
 |  192       CrasUnifiedStream::UnifiedCallback, | 
 |  193       CrasUnifiedStream::StreamError, | 
 |  194       audio_format); | 
 |  195   if (!stream_params) { | 
 |  196     LOG(WARNING) << "Error setting up stream parameters."; | 
 |  197     callback->OnError(this); | 
 |  198     cras_audio_format_destroy(audio_format); | 
 |  199     return; | 
 |  200   } | 
 |  201  | 
 |  202   // Before starting the stream, save the number of bytes in a frame for use in | 
 |  203   // the callback. | 
 |  204   bytes_per_frame_ = cras_client_format_bytes_per_frame(audio_format); | 
 |  205  | 
 |  206   // Adding the stream will start the audio callbacks requesting data. | 
 |  207   if (cras_client_add_stream(client_, &stream_id_, stream_params) < 0) { | 
 |  208     LOG(WARNING) << "Failed to add the stream"; | 
 |  209     callback->OnError(this); | 
 |  210     cras_audio_format_destroy(audio_format); | 
 |  211     cras_client_stream_params_destroy(stream_params); | 
 |  212     return; | 
 |  213   } | 
 |  214  | 
 |  215   // Set initial volume. | 
 |  216   cras_client_set_stream_volume(client_, stream_id_, volume_); | 
 |  217  | 
 |  218   // Done with config params. | 
 |  219   cras_audio_format_destroy(audio_format); | 
 |  220   cras_client_stream_params_destroy(stream_params); | 
 |  221  | 
 |  222   is_playing_ = true; | 
 |  223 } | 
 |  224  | 
 |  225 void CrasUnifiedStream::Stop() { | 
 |  226   if (!client_) | 
 |  227     return; | 
 |  228  | 
 |  229   // Removing the stream from the client stops audio. | 
 |  230   cras_client_rm_stream(client_, stream_id_); | 
 |  231  | 
 |  232   is_playing_ = false; | 
 |  233 } | 
 |  234  | 
 |  235 void CrasUnifiedStream::SetVolume(double volume) { | 
 |  236   if (!client_) | 
 |  237     return; | 
 |  238   volume_ = static_cast<float>(volume); | 
 |  239   cras_client_set_stream_volume(client_, stream_id_, volume_); | 
 |  240 } | 
 |  241  | 
 |  242 void CrasUnifiedStream::GetVolume(double* volume) { | 
 |  243   *volume = volume_; | 
 |  244 } | 
 |  245  | 
 |  246 uint32 CrasUnifiedStream::GetBytesLatency( | 
 |  247     const struct timespec& latency_ts) { | 
 |  248   uint32 latency_usec; | 
 |  249  | 
 |  250   // Treat negative latency (if we are too slow to render) as 0. | 
 |  251   if (latency_ts.tv_sec < 0 || latency_ts.tv_nsec < 0) { | 
 |  252     latency_usec = 0; | 
 |  253   } else { | 
 |  254     latency_usec = (latency_ts.tv_sec * base::Time::kMicrosecondsPerSecond) + | 
 |  255         latency_ts.tv_nsec / base::Time::kNanosecondsPerMicrosecond; | 
 |  256   } | 
 |  257  | 
 |  258   double frames_latency = | 
 |  259       latency_usec * params_.sample_rate() / base::Time::kMicrosecondsPerSecond; | 
 |  260  | 
 |  261   return static_cast<unsigned int>(frames_latency * bytes_per_frame_); | 
 |  262 } | 
 |  263  | 
 |  264 // Static callback asking for samples. | 
 |  265 int CrasUnifiedStream::UnifiedCallback(cras_client* client, | 
 |  266                                        cras_stream_id_t stream_id, | 
 |  267                                        uint8* input_samples, | 
 |  268                                        uint8* output_samples, | 
 |  269                                        unsigned int frames, | 
 |  270                                        const timespec* input_ts, | 
 |  271                                        const timespec* output_ts, | 
 |  272                                        void* arg) { | 
 |  273   CrasUnifiedStream* me = static_cast<CrasUnifiedStream*>(arg); | 
 |  274   return me->DispatchCallback(frames, | 
 |  275                               input_samples, | 
 |  276                               output_samples, | 
 |  277                               input_ts, | 
 |  278                               output_ts); | 
 |  279 } | 
 |  280  | 
 |  281 // Static callback for stream errors. | 
 |  282 int CrasUnifiedStream::StreamError(cras_client* client, | 
 |  283                                    cras_stream_id_t stream_id, | 
 |  284                                    int err, | 
 |  285                                    void* arg) { | 
 |  286   CrasUnifiedStream* me = static_cast<CrasUnifiedStream*>(arg); | 
 |  287   me->NotifyStreamError(err); | 
 |  288   return 0; | 
 |  289 } | 
 |  290  | 
 |  291 // Calls the appropriate rendering function for this type of stream. | 
 |  292 uint32 CrasUnifiedStream::DispatchCallback(size_t frames, | 
 |  293                                            uint8* input_samples, | 
 |  294                                            uint8* output_samples, | 
 |  295                                            const timespec* input_ts, | 
 |  296                                            const timespec* output_ts) { | 
 |  297   switch (stream_direction_) { | 
 |  298     case CRAS_STREAM_OUTPUT: | 
 |  299       return WriteAudio(frames, output_samples, output_ts); | 
 |  300     case CRAS_STREAM_INPUT: | 
 |  301       NOTREACHED() << "CrasUnifiedStream doesn't support input streams."; | 
 |  302       return 0; | 
 |  303     case CRAS_STREAM_UNIFIED: | 
 |  304       return ReadWriteAudio(frames, input_samples, output_samples, | 
 |  305                             input_ts, output_ts); | 
 |  306   } | 
 |  307  | 
 |  308   return 0; | 
 |  309 } | 
 |  310  | 
 |  311 // Note these are run from a real time thread, so don't waste cycles here. | 
 |  312 uint32 CrasUnifiedStream::ReadWriteAudio(size_t frames, | 
 |  313                                          uint8* input_samples, | 
 |  314                                          uint8* output_samples, | 
 |  315                                          const timespec* input_ts, | 
 |  316                                          const timespec* output_ts) { | 
 |  317   DCHECK_EQ(frames, static_cast<size_t>(output_bus_->frames())); | 
 |  318   DCHECK(source_callback_); | 
 |  319  | 
 |  320   uint32 bytes_per_sample = bytes_per_frame_ / params_.channels(); | 
 |  321   input_bus_->FromInterleaved(input_samples, frames, bytes_per_sample); | 
 |  322  | 
 |  323   // Determine latency and pass that on to the source.  We have the capture time | 
 |  324   // of the first input sample and the playback time of the next audio sample | 
 |  325   // passed from the audio server, add them together for total latency. | 
 |  326   uint32 total_delay_bytes; | 
 |  327   timespec latency_ts  = {0, 0}; | 
 |  328   cras_client_calc_capture_latency(input_ts, &latency_ts); | 
 |  329   total_delay_bytes = GetBytesLatency(latency_ts); | 
 |  330   cras_client_calc_playback_latency(output_ts, &latency_ts); | 
 |  331   total_delay_bytes += GetBytesLatency(latency_ts); | 
 |  332  | 
 |  333   int frames_filled = source_callback_->OnMoreIOData( | 
 |  334       input_bus_.get(), | 
 |  335       output_bus_.get(), | 
 |  336       AudioBuffersState(0, total_delay_bytes)); | 
 |  337  | 
 |  338   output_bus_->ToInterleaved(frames_filled, bytes_per_sample, output_samples); | 
 |  339  | 
 |  340   return frames_filled; | 
 |  341 } | 
 |  342  | 
 |  343 uint32 CrasUnifiedStream::WriteAudio(size_t frames, | 
 |  344                                      uint8* buffer, | 
 |  345                                      const timespec* sample_ts) { | 
 |  346   DCHECK_EQ(frames, static_cast<size_t>(output_bus_->frames())); | 
 |  347  | 
 |  348   // Determine latency and pass that on to the source. | 
 |  349   timespec latency_ts  = {0, 0}; | 
 |  350   cras_client_calc_playback_latency(sample_ts, &latency_ts); | 
 |  351  | 
 |  352   int frames_filled = source_callback_->OnMoreData( | 
 |  353       output_bus_.get(), AudioBuffersState(0, GetBytesLatency(latency_ts))); | 
 |  354  | 
 |  355   // Note: If this ever changes to output raw float the data must be clipped and | 
 |  356   // sanitized since it may come from an untrusted source such as NaCl. | 
 |  357   output_bus_->ToInterleaved( | 
 |  358       frames_filled, bytes_per_frame_ / params_.channels(), buffer); | 
 |  359  | 
 |  360   return frames_filled; | 
 |  361 } | 
 |  362  | 
 |  363 void CrasUnifiedStream::NotifyStreamError(int err) { | 
 |  364   // This will remove the stream from the client. | 
 |  365   if (source_callback_) | 
 |  366     source_callback_->OnError(this); | 
 |  367 } | 
 |  368  | 
 |  369 }  // namespace media | 
| OLD | NEW |