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