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